home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / MISC / MAG10.ZIP / MAG10.TXT < prev    next >
Encoding:
Text File  |  1996-09-14  |  145.8 KB  |  3,324 lines

  1. Spell presents:
  2.  
  3.  
  4. ////==\\\\ ||      || //========    |\      /|    //\\     //======\
  5.     ||     ||      || ||            ||\    /||   //  \\   //
  6.     ||     ||      || ||            || \  / ||  //    \\  ||
  7.     ||     ||======|| ||====        ||  \/  ||  ||    ||  ||
  8.     ||     ||      || ||            ||      ||  ||====||  ||    ==\\
  9.     ||     ||      || ||            ||      ||  ||    ||  \\      ||
  10.     ||     ||      || \\========    ||      ||  ||    ||   \\====//
  11.  
  12.                                                         Issue 10
  13.                                                         14-9-96
  14.  
  15.  
  16. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  17.  
  18.  
  19.   Index:
  20.  
  21.         1. Introduction
  22.           1.1. About the magazine
  23.           1.2. About the author
  24.           1.3. Distribution
  25.           1.4. Subscribing
  26.           1.5. Contribuitions
  27.           1.6. Hellos and greets
  28.         2. Putting order into chaos
  29.           2.1. Introduction
  30.           2.2. BubbleSort
  31.           2.3. ShellSort
  32.           2.4. QuickSort
  33.         3. The fire effect
  34.         4. Designing a text adventure - Part III
  35.           4.1. Creating objects
  36.           4.2. Seeing the objects
  37.           4.3. Interacting with objects
  38.         5. The wonderfull world of scrolies !
  39.           5.1. Introduction
  40.           5.2. Simple scroller
  41.           5.3. Sine scroller
  42.           5.4. Mirror and water scroller
  43.         6. First steps into virtual reality
  44.           6.1. Wireframe graphics
  45.           6.2. Basic transformations
  46.           6.3. A 3D starfield
  47.           6.4. VectorBalls
  48.         7. Sprites Part III - Lots of fun stuff
  49.           7.1. Transparency
  50.           7.2. Moving over a background
  51.           7.3. Flipping a sprite
  52.         8. Graphics Part IX - Polygons
  53.           8.1. Simple polygons
  54.           8.2. Filled polygons
  55.           8.3. Ellipses and Arcs
  56.           8.4. Filled Ellipses
  57.         9. Hints and tips
  58.        10. Points of view
  59.        11. The adventures of Spellcaster, part 10
  60.  
  61.  
  62. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  63.  
  64.  
  65.   1. Introduction
  66.  
  67.     1.1. About the magazine
  68.  
  69.     Welcome to super-special issue number 10 !! Why is this issue so special ?
  70.   Well, because it's number 10... I never thought I would get this far, and I
  71.   almost gave up on the 'The Mag' project (see large delay between issues 5
  72.   and 6). But, here it is, to last... :)
  73.     As usual, this is brought by Spellcaster, alias known as Diogo de Andrade.
  74.     What do we have this issue ? Let me see... An article by Scorpio, about
  75.   the fire effect. In the graphics section, I'll talk about polygons and other
  76.   draw tools (I know I said assembler, but I'll leave that for next issue).
  77.   In the Sprites section, I'll talk about transparency, bitmap rotation and
  78.   moving over a background. In 'Designing a text adventure', I'll write about
  79.   objects and object interaction. I'll also do an article on 3D basic
  80.   transformation and some simple effects. Also, an article on different kinds
  81.   of scrollies. On the agenda, I also have an article on sorting...
  82.  
  83.     You should be reading this issue in the first of the SpellUtilities,
  84.   SpellView ! It's a program designed to view text with some features, like
  85.   colors and other neat stuff... :)
  86.     You probably can get it from the same place you got this issue, but if you
  87.   can't, look in the BBS's listed somewhere in this issue, or look in my
  88.   HomePage... It includes full source code, so you can change it, and see how
  89.   it was done... :)
  90.     This can only be probably seen with SpellView V1.4... Get it... It's
  91.   better, bugfixed, etc...
  92.  
  93.     This magazine is dedicated to all the programmers and would-be programmers
  94.   out there, especially to those that can't access the Net easily to get
  95.   valuable information, to those who wish to learn how to program anything,
  96.   from demos to games, passing through utilities and all sort of thing your
  97.   mind can think of, and to those that can't find the right information.
  98.  
  99.     When you read this magazine, I'll assume some things. First, I assume you
  100.   have Borland's Turbo Pascal, version 6 and upwards (and TASM for the assembly
  101.   tutorials). I'll also think you have a 80386 (or 386 for short; a 486 would
  102.   be even better), a load of patience and a sense of humor. This last is almost
  103.   essencial, because I don't receive any money for doing this, so I must have
  104.   fun doing it. I will also take for certain you have the 9th grade (or
  105.   equivelent). Finally, I will assume that you have the last issues of
  106.   'The Mag', and that you have grasped the concepts I tried to transmit. If
  107.   you don't have the issues, you can get them by mail, writing to one of the
  108.   adresses shown below (Snail mail and Email).
  109.  
  110.     As I stated above, this magazine will be made especially for those who don't
  111.   know where to get information, or want it all in the same place, and to those
  112.   who want to learn how to program, so I'll try to build knowledge, building up
  113.   your skills issue by issue. If you sometimes fail to grasp some concept, don't
  114.   despair; try to work it out.
  115.     That's what I did... Almost everything I know was learnt from painfull
  116.   experience. If you re-re-re-read the article, and still can't understand it,
  117.   just drop a line, by mail, or just plain forget it. Most of the things I 
  118.   try to teach here aren't linked to each other (unless I say so), so if you
  119.   don't understand something, skip it and go back to it some weeks later. It
  120.   should be clearer for you then. Likewise, if you see any terms or words you 
  121.   don't understand, follow the same measures as before.
  122.  
  123.     Ok, as I'm earing the Net gurus and other god-like creatures talking
  124.   already, I'm just going to explain why I use Pascal.
  125.   For starters, Pascal is a very good language, ideal for the beginner, like 
  126.   BASIC (yech!), but it's powerfull enough to make top-notch programms.
  127.   Also, I'll will be using assembly language in later issues, and Pascal makes
  128.   it so EASY to use. 
  129.   Finally, if you don't like my choice of language, you can stop whining. The
  130.   teory behind each article is very simple, and common with any of the main
  131.   languages (C, C++, Assembly - Yes, that's true... BASIC isn't a decent
  132.   language).
  133.  
  134.     Just one last thing... The final part of the magazine is a little story
  135.   made up by my distorted mind. It's just a little humor I like to write, and
  136.   it hasn't got nothing to do with programming (well, it has a little), but, 
  137.   as I said before, I just like to write it.
  138.  
  139.     1.2. About the author
  140.  
  141.     Ok, so I'm a little egocentric, but tell me... If you had the trouble of 
  142.   writing hundreds of lines, wouldn't you like someone to know you, even by
  143.   name ?
  144.  
  145.     My name is Diogo de Andrade, alias Spellcaster, and I'm the creator, 
  146.   editor and writer of this magazine. 
  147.     I live in a small town called Setubal, just near Lisbon, the capital of
  148.   Portugal... If you don't know where it is, get an encyclopedia, and look for
  149.   Europe. Then, look for Spain. Next to it, there's Portugal, and Setubal is in
  150.   the middle.
  151.  
  152.     I'm 18 years old, and I just made it in to the university (if you do want
  153.   to know, I'm in the Technical Institute of Lisbon, Portugal), so I'm not 
  154.   a God-Like creature, with dozens of years of practice (I only program by 
  155.   eight years now, and I started in a Spectrum, progressing later to an Amiga.
  156.   I only program in the PC for a year or so), with a mega-computer (I own a 
  157.   386SX, 16 Mhz), that wear glasses with lens that look like the bottom of a 
  158.   bottle (I use glasses, but only sometimes), that has his head bigger than a 
  159.   pumpkin (I have a normal sized head) and with an IQ of over 220 (mine is 
  160.   actually something like 180-190). I can program in C, C++, Pascal, Assembly
  161.   and even BASIC (yech!).
  162.  
  163.     So, if I am a normal person, why do I spend time writing this ?
  164.   Well, because I have the insane urge to write thousands of words every now
  165.   and then, and while I'm at it, I may do something productive, like teaching
  166.   someone. I may be young, but I know a lot about computers (how humble I am;
  167.   I know, modesty isn't one of my qualities).
  168.  
  169.     Just one more thing, if you ever program anything, please send to me... I
  170.   would love to see some work you got, maybe I could learn something with it.
  171.   Also, give me a greet in your program/game/demo... I love seeing my name.
  172.  
  173.     1.3. Distribution
  174.  
  175.     I don't really know when can I do another issue, so, there isn't a fixed
  176.   space of time between two issues. General rule, I will try to do one every
  177.   month, maybe more, probably less (Eheheheh). This is getting to an issue
  178.   every two months, so, I'll think I'll change the above text... :)
  179.     'The Mag' is available by the following means:
  180.  
  181.     - Snail Mail : My address is below, in the Contributions seccion... Just
  182.                    send me a disk and tell me what issues you want, and I
  183.                    will send you them...
  184.  
  185.     - E-Mail : If you E-mail me and ask me for some issues, I will Email you
  186.                back with the relevant issues attached.
  187.  
  188.     - Internet : You can access the Spellcaster page and take the issues out
  189.                  of there in:
  190.  
  191.                  http://alfa.ist.utl.pt/~l42686
  192.  
  193.                  Follow the docs link...
  194.  
  195.     - Anonymous ftp : I've put this issue of 'The Mag' on the ftp.cdrom.com
  196.                       site... I don't know if they'll accept it there, because
  197.                       that's a demo only site, and my mag doesn't cover only
  198.                       demos, but anyways, try it out... It has lots of source
  199.                       code of demos.
  200.  
  201.     - BBS's : You can check out the BBS's list that will carry this and all
  202.               the other issues of 'The Mag' in the file SPELL.DOC.
  203.  
  204.     1.4. Subscribing
  205.  
  206.     If you want, I'm starting "The Mag"'s subscription list... To get
  207.   'The Mag' by Email every month, you just need to mail me and tell me so...
  208.     Then, you will receive it every time a new issue is made...
  209.  
  210.     1.5. Contributions
  211.  
  212.     I as I stated before, I'm not a God... I do make mistakes, and I don't
  213.   have (always) the best way of doing things. So, if you think you've spotted
  214.   an error, or you have thought of a better way of doing things, let me know.
  215.   I'll be happy to receive anything, even if it is just mail saying 'Keep it
  216.   up'. As all human beings, I need incentive.
  217.  
  218.     Also, if you do like to write, please do... Send in articles, they will be
  219.   welcome, and you will have the chance to see your names up in lights.
  220.     They can be about anything, for a review of a book or program that can
  221.   help a programmer, to a point of view or a moan. I'm specially interested in
  222.   articles explaining XMS, EMS, DMA and Soundblaster/GUS.
  223.  
  224.     If anyone out there has a question or wants to see an article about
  225.   something in particular, feel free to write... All letters will be answered,
  226.   provided you give me your address.
  227.  
  228.   If you have a BBS and you want it to include this magazine, feel free to
  229.   write me... I don't have a modem, so I can only send you 'The Mag' by Email.
  230.  
  231.     You can also contact me personally, if you study on the IST (if you don't
  232.   know what the IST is, you don't study there). I'm the freshman with the
  233.   black hair and dark-brown eyes... Yes, the one that in all talkers at the
  234.   same time... :)
  235.  
  236.     My adress is:
  237.                  Praceta Carlos Manito Torres, nº4/6ºC
  238.                  2900 Setúbal
  239.                  Portugal
  240.  
  241.     Email: l42686@alfa.ist.utl.pt
  242.  
  243.     And if you want to contact me on the lighter side, get into the Lost Eden
  244.   talker... To do that telnet to:
  245.  
  246.                         Alfa.Ist.Utl.Pt  : Port 1414
  247.  
  248.     If that server is down, try the Cital talker, in
  249.  
  250.                         Zeus.Ci.Ua.PT    : Port 6969
  251.  
  252.     I'm almost always there in the afternoon... As you may have guessed already,
  253.   my handle is Spellcaster (I wonder why...)...
  254.  
  255.     1.6. Hellos and greets
  256.  
  257.     I'll say hellos and thanks to all my friend, especially for those who put 
  258.   up with my not-so-constant whining.
  259.     Special greets go to Scorpio, Denthor from Asphyxia (for the excelent VGA
  260.   trainers), Draeden from VLA (for assembly tutorials), Dr.Shadow (Delta Team
  261.   is still up), Joao Neves for sugestions, testing and BBS services, Garfield
  262.   (for the 'The Mag' logo and general suport) and all the demo groups out
  263.   there.
  264.     I will also send greets to everybody that responded to my mag... Thank
  265.   you very much !
  266.  
  267.  
  268. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  269.  
  270.  
  271.   2. Putting order into chaos
  272.  
  273.     2.1. Introduction
  274.  
  275.     Welcome to another article by your friend Spellcaster... This article is
  276.   about sorting an array... What's sorting, you may ask ?
  277.   [ Everyone asks what's sorting !! ]
  278.     Well, sorting is organizing data in a data structure... Get it ?
  279.   [ Everybody says no... ]
  280.     Ok, imagine you have an array like this:
  281.  
  282.                 A[1]:=221;
  283.                 A[2]:=321;
  284.                 A[3]:=831;
  285.                 A[4]:=432;
  286.                 A[5]:=721;
  287.  
  288.     Sorting is converting that array into:
  289.  
  290.                 A[1]:=221;
  291.                 A[2]:=321;
  292.                 A[3]:=432;
  293.                 A[4]:=721;
  294.                 A[5]:=831;
  295.  
  296.     See ? Sorting is the same as ordering...
  297.     There are _LOTS_ of methods for doing this, but there are some that are
  298.   'standart'... Each one has it's advantages and disadvantages... I'll talk
  299.   about three methods... I'll start with:
  300.  
  301.     2.2. BubbleSort
  302.  
  303.     BubbleSort is the slowest and easiest to program method, and it is sometimes
  304.   the best one for small and desordered arrays.
  305.     The ideia of BubbleSort is to order an array by swapping the contents of
  306.   two adjacent memory positions (if they are out of order). A flag assures that
  307.   the array is ordered. Execution example:
  308.  
  309.     We want to sort the array [ 7 4 2 3 5 1 6 ]. Let's see:
  310.  
  311.     ┌──────┬───────────────┬────────────────────────────────────────────┐
  312.     │ Step │ Array         │ Comment                                    │
  313.     ├──────┼───────────────┼────────────────────────────────────────────┤
  314.     │   0  │ 7 4 2 3 5 1 6 │ The beggining                              │
  315.     │   1  │ 4 7 2 3 5 1 6 │ Because 4<7, then 4 and 7 are exchanged    │
  316.     │   2  │ 4 2 7 3 5 1 6 │ Because 2<7, then 2 and 7 are exchanged    │
  317.     │   3  │ 4 2 3 7 5 1 6 │ Because 3<7, then 3 and 7 are exchanged    │
  318.     │   4  │ 4 2 3 5 7 1 6 │ Because 5<7, then 5 and 7 are exchanged    │
  319.     │   5  │ 4 2 3 5 1 7 6 │ Because 1<7, then 1 and 7 are exchanged    │
  320.     │   6  │ 4 2 3 5 1 6 7 │ Because 6<7, then 6 and 7 are exchanged    │
  321.     │      │               │ As the array isn't ordered yet, the process│
  322.     │      │               │ restarts:                                  │
  323.     │   7  │ 2 4 3 5 1 6 7 │ Because 2<4, then 2 and 4 are exchanged    │
  324.     │   8  │ 2 3 4 5 1 6 7 │ Because 3<4, then 3 and 4 are exchanged    │
  325.     │   9  │ 2 3 4 5 1 6 7 │ Because 5>4, the array remains the same    │
  326.     │  10  │ 2 3 4 1 5 6 7 │ Because 1<5, then 1 and 5 are exchanged    │
  327.     │  ..  │ ............. │ .........................................  │
  328.     │      │ 1 2 3 4 5 6 7 │                                            │
  329.     └──────┴───────────────┴────────────────────────────────────────────┘
  330.  
  331.     You should get the ideia by now... So, let's do a procedure that
  332.   implements the above explained algorithm. The example program orders an array
  333.   with 40 random inserted elements, generated from a seed (read this issue's
  334.   hints and tips). The program also keeps tracks on how many comparissons and
  335.   exchanges it does, so we can compare this type of sort with the others,
  336.   because that's the way to evaluate the algorithm's overall eficiency.
  337.   So, without further due, here's the code:
  338.  
  339.                 Program BubbleSort;
  340.  
  341.                 Uses Crt;
  342.  
  343.                 Type DataType=Array[1..40] Of Integer;
  344.  
  345.                 Var Data:DataType;
  346.                     A:Byte;
  347.                     Compare:Word;
  348.                     XChange:Word;
  349.  
  350.                 Procedure FillData;
  351.                 Begin
  352.                      RandSeed:=122217;        { Random seed }
  353.                      For A:=1 To 40 Do Data[A]:=Random(1000);
  354.                 End;
  355.  
  356.                 Procedure WriteArray;
  357.                 Var B:Byte;
  358.                 Begin
  359.                      For A:=0 To 3 Do
  360.                      Begin
  361.                           For B:=1 To 10 Do
  362.                           Begin
  363.                                GotoXY(B*6+5,A+10);
  364.                                Write(Data[A*10+B]);
  365.                           End;
  366.                           Writeln;
  367.                      End;
  368.                 End;
  369.  
  370.                 Procedure Sort;
  371.                 Var Flag:Boolean;
  372.                     C:Integer;
  373.                 Begin
  374.                      Compare:=0;
  375.                      XChange:=0;
  376.                      Repeat
  377.                            Flag:=True;
  378.                            For A:=1 To 39 Do
  379.                            Begin
  380.                                 If Data[A]>Data[A+1] Then
  381.                                 Begin
  382.                                      { Exchange data }
  383.                                      C:=Data[A];
  384.                                      Data[A]:=Data[A+1];
  385.                                      Data[A+1]:=C;
  386.                                      Flag:=False;
  387.                                      { Increase exchanges counter }
  388.                                      Inc(XChange);
  389.                                 End;
  390.                                 { Increase comparisons counter }
  391.                                 Inc(Compare);
  392.                            End;
  393.                      Until Flag;
  394.  
  395.                 End;
  396.  
  397.                 Begin
  398.                      Clrscr;
  399.                      FillData;
  400.                      GotoXY(13,8);
  401.                      Writeln('Array before BubbleSort:');
  402.                      WriteArray;
  403.                      ReadLn;
  404.                      Sort;
  405.                      Clrscr;
  406.                      GotoXY(13,8);
  407.                      WriteLn('Array after BubbleSort:');
  408.                      WriteArray;
  409.                      GotoXY(13,16);
  410.                      WriteLn('Compares=',Compare);
  411.                      GotoXY(13,17);
  412.                      Writeln('Exchanges=',XChange);
  413.                      ReadLn;
  414.                 End.
  415.  
  416.     Well, this is the easiest kind of sort... Let's move on to the next:
  417.  
  418.     2.3. ShellSort
  419.  
  420.     ShellSort is the most complex (for me, at least) way of sorting...
  421.     The problem with BubbleSort is that it exchanges values to near to each
  422.   other... If you check the example array [ 7 4 2 3 5 1 6 ], notice that
  423.   the number 7 must traverse almost the whole array to reach it's final
  424.   position. Wouldn't it be great to make it go to it's position in 2 steps,
  425.   instead of the 6 it requires ?
  426.     Well, the ideia of ShellSort is to have an incremental factor... In reality,
  427.   ShellSort is a special case of BubbleSort, a case where the elements that
  428.   are compared/exchanged aren't so near to each other. The number of
  429.   comparissons will sometimes be the same or bigger than BubbleSort, but the
  430.   number of exchanges will be less... So, let's see an example:
  431.     The increment selected is 4:
  432.  
  433.     ┌──────┬───────────────┬────────────────────────────────────────────┐
  434.     │ Step │ Array         │ Comment                                    │
  435.     ├──────┼───────────────┼────────────────────────────────────────────┤
  436.     │   0  │ 7 4 2 3 5 1 6 │ The beggining                              │
  437.     │   1  │ 5 4 2 3 7 1 6 │ Because 5<7, 5 and 7 are exchanged         │
  438.     │   2  │ 5 1 2 3 7 4 6 │ Because 1<4, 1 and 4 are exchanged         │
  439.     │   3  │ 5 1 2 3 7 4 6 │ Because 2<6, the array remains unaltered   │
  440.     └──────┴───────────────┴────────────────────────────────────────────┘
  441.  
  442.     If you look at the above example and think 'This demonstration isn't
  443.   ended !!', you are wrong... This demonstration is more than finished.
  444.   ShellSort requires several diferent increments, finalizing with the
  445.   increment of 1. So, let's now adapt the array we've obtained with ShellSort
  446.   with increment of 2:
  447.  
  448.     ┌──────┬───────────────┬────────────────────────────────────────────┐
  449.     │ Step │ Array         │ Comment                                    │
  450.     ├──────┼───────────────┼────────────────────────────────────────────┤
  451.     │   0  │ 5 1 2 3 7 4 6 │ The beggining                              │
  452.     │   1  │ 2 1 5 3 7 4 6 │ Because 2<5, 2 and 5 are exchanged         │
  453.     │   2  │ 2 1 5 3 7 4 6 │ Because 1<3, the array remains unaltered   │
  454.     │   3  │ 2 1 5 3 7 4 6 │ Because 5<7, the array remains unaltered   │
  455.     │   4  │ 2 1 5 3 7 4 6 │ Because 3<4, the array remains unaltered   │
  456.     │   5  │ 2 1 5 3 6 4 7 │ Because 6<7, 6 and 7 are exchenged         │
  457.     └──────┴───────────────┴────────────────────────────────────────────┘
  458.  
  459.     Well, the array isn't ordered already, but notice that the number 7 is
  460.   already in the correct position... Now, you should apply ShellSort with
  461.   increment of 1 to the array obtained from the previous ShellSorts. That is
  462.   equal of applying BubbleSort. So, let's check out an example program...
  463.   Again it keeps tracks of the numbers of comparissons and exchanges made:
  464.  
  465.                 Program ShellSort;
  466.  
  467.                 Uses Crt;
  468.  
  469.                 Type DataType=Array[1..40] Of Integer;
  470.  
  471.                 Var Data:DataType;
  472.                     A:Byte;
  473.                     Compare:Word;
  474.                     XChange:Word;
  475.  
  476.                 Procedure FillData;
  477.                 Begin
  478.                      RandSeed:=122217;        { Random seed }
  479.                      For A:=1 To 40 Do Data[A]:=Random(1000);
  480.                 End;
  481.  
  482.                 Procedure WriteArray;
  483.                 Var B:Byte;
  484.                 Begin
  485.                      For A:=0 To 3 Do
  486.                      Begin
  487.                           For B:=1 To 10 Do
  488.                           Begin
  489.                                GotoXY(B*6+5,A+10);
  490.                                Write(Data[A*10+B]);
  491.                           End;
  492.                           Writeln;
  493.                      End;
  494.                 End;
  495.  
  496.                 Procedure Sort(H:Integer);
  497.                 Var Flag:Boolean;
  498.                     B,C:Integer;
  499.                 Begin
  500.                      For A:=1+H To 40 Do
  501.                      Begin
  502.                           C:=Data[A];
  503.                           B:=A;
  504.                           While (B-H>0) And (Data[B-H]>C) Do
  505.                           Begin
  506.                                Data[B]:=Data[B-H];
  507.                                B:=B-H;
  508.                                Inc(Compare,2);
  509.                                Inc(XChange);
  510.                           End;
  511.                           Data[B]:=C;
  512.                      End;
  513.                 End;
  514.  
  515.                 Begin
  516.                      Clrscr;
  517.                      FillData;
  518.                      GotoXY(13,8);
  519.                      Writeln('Array before ShellSort:');
  520.                      WriteArray;
  521.                      ReadLn;
  522.                      Compare:=0;
  523.                      XChange:=0;
  524.                      Sort(4);
  525.                      Clrscr;
  526.                      GotoXY(13,8);
  527.                      WriteLn('Array after ShellSort (increment 4):');
  528.                      WriteArray;
  529.                      ReadLn;
  530.                      Clrscr;
  531.                      Sort(2);
  532.                      Clrscr;
  533.                      GotoXY(13,8);
  534.                      WriteLn('Array after ShellSort (increment 2):');
  535.                      WriteArray;
  536.                      ReadLn;
  537.                      Clrscr;
  538.                      Sort(1);
  539.                      Clrscr;
  540.                      GotoXY(13,8);
  541.                      WriteLn('Array after ShellSort (increment 1):');
  542.                      WriteArray;
  543.                      GotoXY(13,16);
  544.                      WriteLn('Compares=',Compare);
  545.                      GotoXY(13,17);
  546.                      Writeln('Exchanges=',XChange);
  547.                      ReadLn;
  548.                 End.
  549.  
  550.     Just compare the difference between the two sorts... If you are a little
  551.   confused by the example program, reread it, keeping in mind that:
  552.  
  553.            1) The program goes from end to beggining of the array
  554.            2) The program finds the best position for one value. For example:
  555.  
  556.                   Original Array: [ 7 2 3 1 5 4 ]
  557.  
  558.               If you use the original algorithm I talked about, then the array
  559.               would look like this after a step (increment 2):
  560.  
  561.                                   [ 3 2 7 1 5 4 ]
  562.  
  563.               While the new algorithm makes the array like this:
  564.  
  565.                                   [ 3 2 5 1 7 4 ]
  566.  
  567.               In just one step, because it 'sticks' to number 7 until it can
  568.               move it no further...
  569.  
  570.     Well, but what is the best sequence of increments ? There's just one rule
  571.   for that... They shouldn't be multiples of each others... If it's a small
  572.   array, that doesn't influenciate, but if it is a large array (i.e. more than
  573.   5000 positions), it does make a very big difference.
  574.     This is lots of times faster than BubbleSort, but still it is slower than:
  575.  
  576.     2.4. QuickSort
  577.  
  578.     This is harder to compreend for the begginer, because it is better done in
  579.   a recursive form (see this issue's tricks and tips).
  580.     So, QuickSort plays around with the ideia of partitioning the array is
  581.   several smaller arrays, putting some order in each of the partitions.
  582.     QuickSort doesn't really split the array in other arrays (that would need
  583.   more memory). It just works with a part of the array at a time.
  584.     To exemplify this one, I have to aquire another way to make the program's
  585.   trace. So, assume that I and J as two variables that traverse the array, and
  586.   imagine that the array we want to order is [ 44 55 12 42 94 18 6 67 ]
  587.     So, the first thing to do in QuickSort is to select any number. Usually
  588.   the number is in the array, altough this is not necessary. Let's choose the
  589.   number 26. So, let's order the array in a way that all numbers smaller than
  590.   26 go to the beggining of the array (the left) and all the numbers that are
  591.   bigger go to the end of the array (the right). So:
  592.  
  593.          Step 0:   44 55 12 42 94 18 06 67
  594.                    ^                  ^
  595.                    I                  J
  596.  
  597.     So, what the algorithm does is to put I pointing to the first number it
  598.   founds from the beggining of the array, larger than the select number (in
  599.   this case 26). So, I points to 44. And we point J to the first number smaller
  600.   than 26, counting from the right. In this case, J will point to 06.
  601.     Then it switches the contents of both positions:
  602.  
  603.          Step 1:   06 55 12 42 94 18 44 67
  604.                       ^            ^
  605.                       I            J
  606.  
  607.     And I and J find the next numbers than fullfill the caracteristics we've
  608.   discussed previously. So, step after step:
  609.  
  610.          Step 2:   06 18 12 42 94 55 44 67
  611.                           ^ ^
  612.                           J I
  613.  
  614.     When J is smaller than I, that means that we have a 'final' array:
  615.  
  616.                       [ 6 18 12 42 94 55 44 67 ]
  617.  
  618.     That we can think as two arrays:
  619.  
  620.                       [ 6 18 12 ] + [ 42 94 55 44 67 ]
  621.  
  622.     One with elements smaller than 26 and the other with elements bigger than
  623.   26. So, we apply the same method to each one of the sub-arrays, until we
  624.   reach subarrays with only one element. Then, if you 'sum' them all up, you'll
  625.   get an ordered array. Check out the example program. In the example program,
  626.   we'll choose the base number (the 26 in our previous example) as beeing the
  627.   number halfway in the array (in the above example, it would be number 42 or
  628.   94):
  629.  
  630.                 Program QuickSort;
  631.  
  632.                 Uses Crt;
  633.  
  634.                 Type DataType=Array[1..40] Of Integer;
  635.  
  636.                 Var Data:DataType;
  637.                     A:Byte;
  638.                     Compare:Word;
  639.                     XChange:Word;
  640.  
  641.                 Procedure FillData;
  642.                 Begin
  643.                      RandSeed:=122217;        { Random seed }
  644.                      For A:=1 To 40 Do Data[A]:=Random(1000);
  645.                 End;
  646.  
  647.                 Procedure WriteArray;
  648.                 Var B:Byte;
  649.                 Begin
  650.                      For A:=0 To 3 Do
  651.                      Begin
  652.                           For B:=1 To 10 Do
  653.                           Begin
  654.                                GotoXY(B*6+5,A+10);
  655.                                Write(Data[A*10+B]);
  656.                           End;
  657.                           Writeln;
  658.                      End;
  659.                 End;
  660.  
  661.                 Procedure Sort;
  662.                 Var Flag:Boolean;
  663.                     I,J:Integer;
  664.                     X,N:Integer;
  665.  
  666.                     Procedure SortSubArray(Left,Right:Byte);
  667.                     Begin
  668.                          { Partition }
  669.                          I:=Left;
  670.                          J:=Right;
  671.                          N:=Data[(Left+Right) Div 2];
  672.                          Repeat
  673.                                { Find first number from the left to be < N }
  674.                                While Data[I]<N Do
  675.                                Begin
  676.                                     Inc(Compare);
  677.                                     Inc(I);
  678.                                End;
  679.                                { Find first number from the right to be > N }
  680.                                While Data[J]>N Do
  681.                                Begin
  682.                                     Inc(Compare);
  683.                                     Dec(J);
  684.                                End;
  685.                                { Exchange }
  686.                                If I<=J Then
  687.                                Begin
  688.                                     X:=Data[J];
  689.                                     Data[J]:=Data[I];
  690.                                     Data[I]:=X;
  691.                                     Inc(I);
  692.                                     Dec(J);
  693.                                     Inc(XChange);
  694.                                End;
  695.                                Inc(Compare,2);
  696.                          Until J<I;
  697.                          { Order left and right subarrays }
  698.                          If Left<J Then SortSubArray(Left,J);
  699.                          If I<Right Then SortSubArray(I,Right);
  700.                          Inc(Compare,2);
  701.                     End;
  702.  
  703.                 Begin
  704.                      SortSubArray(1,40);
  705.                 End;
  706.  
  707.                 Begin
  708.                      Clrscr;
  709.                      FillData;
  710.                      GotoXY(13,8);
  711.                      Writeln('Array before QuickSort:');
  712.                      WriteArray;
  713.                      ReadLn;
  714.                      Compare:=0;
  715.                      XChange:=0;
  716.                      Sort;
  717.                      Clrscr;
  718.                      GotoXY(13,8);
  719.                      WriteLn('Array after QuickSort:');
  720.                      WriteArray;
  721.                      GotoXY(13,16);
  722.                      WriteLn('Compares=',Compare);
  723.                      GotoXY(13,17);
  724.                      Writeln('Exchanges=',XChange);
  725.                      ReadLn;
  726.                 End.
  727.  
  728.     QuickSort is only good when the value selected as the 'medium value' is
  729.   good... Or else, it sucks ! :)
  730.     So, let's compare the three sorts:
  731.  
  732.                            BubbleSort     ShellSort     QuickSort
  733.  
  734.               Compares        1404           404           498
  735.               Exchanges        430           202            78
  736.  
  737.     See ?! Altough ShellSort does fewer comparisons, it does almost 3 times
  738.   more exchanges, which is a heavier operation. So, QuickSort is in fact
  739.   a lot faster.
  740.  
  741.     Sorts are very important for programs, for demos and games... You'll see
  742.   an aplication of QuickSort in one of the articles this month (the 3d article,
  743.   the part about vectorballs). So, see you in another article... :)
  744.  
  745.  
  746. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  747.  
  748.  
  749.   3. The fire effect
  750.  
  751.     Welcome to this fine article written by Scorpio... I, Spellcaster, will
  752.   make any comments between square brackets. Oh, by the way, I changed
  753.   Scorpio's code a bit, in order to be compatible with the Mode13h Unit I'm
  754.   giving you issue by issue... Sorry, Scorpio... I also cleaned up a bit his
  755.   comments... But the code was made by him... :) Take the lead, Scorpio...
  756.  
  757.     Hi there. It's Scorpio again (??). This time, I am explaining you how you
  758.   can burn down your monitor... Yep, I know... You can always dump gas on it
  759.   and then light a match... But I want it to be a GOOD fire... one that doesn't
  760.   actually burn your monitor, but one that actually will be so close to reality
  761.   that'll make you feel hotter just being near the monitor... :)))))
  762.     Enough of this... OK... There are LOTS of fires around the world that are
  763.   FASTER, SMALLER (check #coderz 256 byte fire compo) and more beautifull than
  764.   the one I'll show you, but mine is A LOT EASIER to understand!!! Let's hear
  765.   (read) some stuff on the fire algorythm.
  766.  
  767.   o The palette
  768.  
  769.     To have a good-looking fire, you NEED a good palette... One that goes
  770.     from black to orange and then to red...(256 color pal, of courz)
  771.  
  772.     [ You can also use a pallete that goes from black to white, then to yellow,
  773.       then to orange and finally to red... Scorpio's original palette is in
  774.       the file FIRE.PAL, but I've made another one, and put it in the
  775.       FIRE2.PAL file... Try it out... Just change the name in the source to
  776.       see it... ]
  777.  
  778.   o The Get/Putpixel functions
  779.  
  780.     Well... These functions will be used A LOT in the fire... Get yours
  781.     instead of the one I have in the sources...(YEAH, I know that yourz
  782.     can do 5.000.000.000 put/getpixel in 0.0000001 msec.. :-) )). Just
  783.     use one that is fast enough.
  784.  
  785.   o The average-for-some-but-not-all-points-around-the-current function
  786.  
  787.     This is just a function that calculates the average of the colors on
  788.     SOME (let's say 4) pixels around one pixel...
  789.  
  790.     OK... It seems I have a lot of explaining to do... First thing comes first
  791.   The palette has got to have some colours that are usually on a fire, 'cos if
  792.   you make a fire with green and blue it won't look like a fire, will it? Try
  793.   to make a 'gradient' from black to orange and then to red that looks smooth,
  794.   with no violent color-transitions... This way, the fire will look like a fire.
  795.     Now, the get/putpixel functions... I have a SIMPLE MEM get/putpixel function
  796.   'cos EVERYBODY knows it and so there won't be any misunderstanding 'cos of a
  797.   get/putpixel... To have a nicer effect, simply cut'n'paste YOUR get/putpixel
  798.   functions to the source and it'll work just fine...
  799.     Last but not least, comes the average function...This is a function that
  800.   received 4 (see explanation below) numbers and gives the average of those
  801.   4 numbers.
  802.  
  803.     Finally... Yeah...I'm writting this at 3 a.m. and my fingers are
  804.   starting to complain... :) How does this work??? First of all, get to
  805.   mode 13h (??). Okay... No, I'm not joking... Now here goes a pseudo-lang
  806.   piece of code:
  807.  
  808.   TOP-OF-LOOP:    put-some-random-colours in bottom line of fire
  809.                   from Y:=bottom-line to upper-line do
  810.                     from X:=left-side-of-fire to right-side-of-fire do
  811.                       AVG:=average(point1,point2,point3,point4);
  812.                       putpixel(X,Y-1,AVG);
  813.                   return to TOP-OF-LOOP
  814.  
  815.  
  816.     "Okay... that doesn't seem too hard to to... but WHAT are point1..point4",
  817.   you might say... And my answer is: This is the magic of the fire algorythm...
  818.   In the source, you'll see that I chose the point itself, the point at its
  819.   right,the point at its left and the point above it... Why did I choose these
  820.   4 points? These ones seem to give me fine results... Try some other ones...
  821.     Why do I store the average ABOVE (y-1) the current pixel?? I do it so that
  822.   the fire goes up, i.e. the fire seems to go up but fading a little (the flames
  823.   in the bottom are brighter).
  824.     "HEY! I checked your source and it has different positions to put the
  825.   average of the current pixel..." Yes, it has... Please notice that the fire
  826.   at its upper part starts to have a bit more of movement. You can see something
  827.   like this in the sources:
  828.  
  829.                    MEM[VGA:320*y+x+random(3)-1]:=colour
  830.  
  831.     This gives a little bit of movement to the fire, and it looks G*R*E*A*T!!
  832.   Try it yourself, and choose one that looks good to what you want to do. You
  833.   have some more ways to do it in the source... It is V*E*R*Y well documented.
  834.  
  835.   [ What happened to modesty ???? :) ]
  836.  
  837.     It looks like it is the end of this article... As always (IM), if you have
  838.   any problems with it, contact me at si17899@ci.uminho.pt.. If you want to
  839.   tell me that my code sucks, email me at logname@hostname :-).
  840.  
  841.  
  842. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  843.  
  844.  
  845.   4. Designing a text adventure - Part III
  846.  
  847.     In this issue's tutorial in text adventures, we'll delve into the amazing
  848.   and essencial world of objects !!!
  849.     No text adventure would be complete without 100000 objects lying around
  850.   the gamescape! For FangLore, we'll be more modest, and we'll just have four
  851.   objects... These are:
  852.  
  853.              -> Shovel   - Needed to knock the door down
  854.              -> Mask     - Needed to pass the room with the gas
  855.              -> Sword    - Needed to kill the monsters
  856.              -> Treasure - Guess ?!
  857.  
  858.     You can include other redundanct objects, without any purpose, except to
  859.   confuse, or maybe amuse, the player... Let's put in the game 2 redundact
  860.   objects:
  861.              -> Lazer gun     - A prop from a previous game
  862.              -> A strange key - No purpose at all !
  863.  
  864.     So, lets create the objects...
  865.  
  866.     4.1. Creating objects
  867.  
  868.     To do so, we can use the program suplied (OBJGEN.PAS). It is very similar
  869.   to the ROOMGEN.PAS program I gave with issue 8, except that this one creates
  870.   rooms, while the OBJGEN.PAS program creates the objects.
  871.     So, what do we need to store ?
  872.     Well, it's name, it's position (we can move objects from a room to another,
  873.   or be carrying it) and it's description... I'll explain the description part
  874.   latter. So, let's define a record like this:
  875.  
  876.               Type ObjType=Record
  877.                                  Name:String[80];
  878.                                  Pos:Byte;
  879.                                  Desc:Array[1..5] Of String[80];
  880.                            End;
  881.  
  882.     And let's define the number of objects in the game:
  883.  
  884.               Const NumberObjs=6;
  885.  
  886.     And, finally, let's define the array that will store the objects:
  887.  
  888.               Var Objects:Array[1..NumberObjs] of ObjType;
  889.  
  890.     So, after we've defined these data structures and made a file called
  891.   OBJ.DAT (with the OBJGEN program) with the data, let's read the data, with
  892.   a procedure similar in every respect with the ReadRoomData procedure that
  893.   I gave in the first article of this series:
  894.  
  895.          Procedure ReadObjData;     { Read from the disk the object data }
  896.          Var F:Text;
  897.              A,B:Byte;
  898.              Flag:Boolean;
  899.          Begin
  900.               { Prepares the text file for accessing }
  901.               Assign(F,'Obj.Dat');
  902.               Reset(F);
  903.               { For every object in the game }
  904.               For A:=1 To NumberObjs Do
  905.               Begin
  906.                    { Read the name of the objects }
  907.                    ReadLn(F,Objects[A].Name);
  908.                    { Read the initial position of the objects }
  909.                    ReadLn(F,Objects[A].Pos);
  910.                    { Clear the object's description }
  911.                    For B:=1 To 5 Do Objects[A].Desc[B]:='';
  912.                    { Read the description of the room }
  913.                    Flag:=True;
  914.                    B:=1;
  915.                    While Flag Do
  916.                    Begin
  917.                         ReadLn(F,Objects[A].Desc[B]);
  918.                         If (B=5) Or (Objects[A].Desc[B]='*') Then Flag:=False;
  919.                         Inc(B);
  920.                    End;
  921.               End;
  922.               Close(F);
  923.          End;
  924.  
  925.     Just a note... If the POS field of the any object is equal to 0, that means
  926.   that we are carrying the object, and if it is equal to 255, that means that
  927.   the object is hidden (case of the gas mask, that only appears if the oven is
  928.   open).
  929.  
  930.     4.2. Seeing the objects
  931.  
  932.     An object is worthless, unless you can use it... And you can only use it
  933.   if you can see it, to know it is there... So, there are two 'kinds' of
  934.   objects you can see... Objects you own and objects that are lying around...
  935.   A good game has different descriptions for both the 'kinds', but Fanglore
  936.   just does the same thing in either case: spit out the description.
  937.     So, there is a basic command that is equal in _EVERY_ text adventure in the
  938.   world: EXAMINE !
  939.     The examine command has a parameter following, that is, it accepts (and
  940.   requires) that you type another thing... That thing is the thing you want
  941.   to examine... For example, if you wanted to examine the sword, you would
  942.   type:
  943.                                 EXAMINE SWORD
  944.  
  945.     So, EXAMINE is the command, and SWORD is the parameter. But notice that
  946.   you aren't limited to just examine objects... You can examine anything in
  947.   the game... For example, the oven in the kitchen... But I'll talk about that
  948.   later, in another article of this serie...
  949.     Also, notice that you can only examine objects that you have or that are
  950.   in the same room as you... So, let's check out the EXAMINE procedure:
  951.  
  952.          Procedure Examine(Param:String);
  953.          Var Flag:Boolean;
  954.              A,B:Byte;
  955.          Begin
  956.               Flag:=True;
  957.               { ***************************************************** }
  958.               { Here we'll include code for things like the oven, etc }
  959.               { ***************************************************** }
  960.               { Search the object in the object list }
  961.               A:=0;
  962.               Repeat
  963.                    Inc(A);
  964.                    If Objects[A].Name=Param Then Flag:=True;
  965.               Until Flag Or (A>NumberObjs);
  966.               If Flag Then
  967.               Begin
  968.                    { The object exists }
  969.                    { Check if it is in the room or in your possession }
  970.                    If (Objects[A].Pos=0) Or (Objects[A].Pos=CurrentRoom) Then
  971.                    Begin
  972.                         { The object is 'visible'... Write description }
  973.                         Writeln;
  974.                         B:=1;
  975.                         TextColor(Yellow);
  976.                         While (B<6) And (Objects[A].Desc[B]<>'*') Do
  977.                         Begin
  978.                              Writeln(Objects[A].Desc[B]);
  979.                              Inc(B);
  980.                         End;
  981.                         Writeln;
  982.                    End
  983.                    Else
  984.                    Begin
  985.                         { The object exists, but it's not visible }
  986.                         Writeln;
  987.                         TextColor(Yellow);
  988.                         WriteLn('I can''t see the ',Param,' here...');
  989.                         WriteLn;
  990.                    End;
  991.               End
  992.               Else
  993.               Begin
  994.                    { The specified parameter doesn't represent any existing }
  995.                    { object...                                              }
  996.                    WriteLn;
  997.                    TextColor(Yellow);
  998.                    WriteLn('Sorry, but I don''t even know what is a ',Param);
  999.                    WriteLn;
  1000.               End;
  1001.          End;
  1002.  
  1003.     Of course you'll have to do the routine that calls this procedure... You
  1004.   must put it (as usual with all commands) in the Play procedure. The routine
  1005.   is like this:
  1006.  
  1007.                 If Parsed[1]='EXAMINE' Then
  1008.                 Begin
  1009.                      Valid:=True;
  1010.                      Examine(Parsed[2]);
  1011.                 End;
  1012.  
  1013.     Simple, isn't it... But this is not over... If you remember what I told
  1014.   you about parsing, you know that the phrases are splitted in words. But,
  1015.   Gas Mask has two words in it's name... So, the program can't cope with
  1016.   objects with composite names. How can we make him accept it ?
  1017.   Well, we have to join the words again... Like this:
  1018.  
  1019.                 If Parsed[1]='EXAMINE' Then
  1020.                 Begin
  1021.                      Valid:=True;
  1022.                      { Join the words again }
  1023.                      D:=3;
  1024.                      E:=Parsed[2];
  1025.                      While Parsed[D]<>'' Do
  1026.                      Begin
  1027.                           E:=E+' '+Parsed[D];
  1028.                           Inc(D);
  1029.                      End;
  1030.                      Examine(E);
  1031.                 End;
  1032.  
  1033.     You must define E as a string and D as a Byte types of variables.
  1034.     Altough this works with commands that only require one parammeter, this
  1035.   fails with other commands were multiple parameters are required... So,
  1036.   general rule, don't use objects with names that have spaces...
  1037.  
  1038.     But this section isn't finished yet... We still have to know what objects
  1039.   are in the room, and what objects do you carry... But I'll leave that for
  1040.   tomorrow, because I had 2 hours sleep last night... And I'm VERY tired...
  1041.     Good night... Zzzzzzzzzzzzzzzzzzzzzzzz..........
  1042.  
  1043.     Ok... I'm back ! :) Ready for more... So... The problem this moment is
  1044.   knowing what are the objects we can interact. That's easy. When you enter
  1045.   a room, you make the program print out a list of visible objects. To do
  1046.   so, just add this code to the Look procedure:
  1047.  
  1048.                TextColor(LightCyan);
  1049.                Writeln('Visible objects:');
  1050.                Flag:=False;
  1051.                For A:=1 To NumberObjs Do If Objects[A].Pos=RoomNumber Then
  1052.                Begin
  1053.                     Writeln('         ',Objects[A].Name);
  1054.                     Flag:=True;
  1055.                End;
  1056.                If Flag=False Then Writeln('          None');
  1057.  
  1058.     Don't forget do define the Flag variable as a Boolean. It is used to know
  1059.   if there is any objects in the room... If it is false after the For loop
  1060.   ends, that means that there aren't any objects in the room, so the program
  1061.   writes down that it doesn't see any objects...
  1062.  
  1063.     To know what objects you are carrying, you can use second best-known
  1064.   command in text adventures... The INVENTORY command !
  1065.     To do the inventory command, you just check all the objects in the game.
  1066.     If their position (the POS field) is equal to zero, then you are carrying
  1067.   it... Code:
  1068.  
  1069.              Procedure Inventory;
  1070.              Var A:Byte;
  1071.                  Flag:Boolean;
  1072.              Begin
  1073.                   Flag:=False;
  1074.                   TextColor(LightBlue);
  1075.                   WriteLn;
  1076.                   WriteLn('Objects carried:');
  1077.                   For A:=1 To NumberObjs Do If Objects[A].Pos=0 Then
  1078.                   Begin
  1079.                        WriteLn('                ',Objects[A].Name);
  1080.                        Flag:=True;
  1081.                   End;
  1082.                   If Flag=False Then Writeln('                 None');
  1083.                   WriteLn;
  1084.              End;
  1085.  
  1086.     As usual, you have to add some code to the Play procedure, that calls the
  1087.   above procedure:
  1088.  
  1089.                   If Parsed[1]='INVENTORY' Then
  1090.                   Begin
  1091.                        Valid:=True;
  1092.                        Inventory;
  1093.                   End;
  1094.  
  1095.     So, now you look at objects... You just have to do some interaction...
  1096.  
  1097.     4.3. Interacting with objects
  1098.  
  1099.     An object is useless unless you can you interact somehow with it... There
  1100.   are three common interactions you can do with objects: Get, Drop and Use.
  1101.     Let's start with the easy ones: Get and Drop
  1102.     To get an object, all the program has to do is to set the POS field of
  1103.   the desired object to zero... After we verify if the object exists and if
  1104.   it is in the room, and if it isn't in your position already.
  1105.     To drop an object, you just have have to verify if you have that object
  1106.   and set the POS field of the object to the number of the room you're on...
  1107.     Coding wise:
  1108.  
  1109.                 Procedure Get(O:String);
  1110.                 Var A:Byte;
  1111.                     Flag:Boolean;
  1112.                 Begin
  1113.                      Flag:=False;
  1114.                      A:=0;
  1115.                      Repeat
  1116.                            Inc(A);
  1117.                            If O=Objects[A].Name Then Flag:=True;
  1118.                      Until (A>NumberObjs) Or (Flag=True);
  1119.                      If Flag=True Then
  1120.                      Begin
  1121.                           { The object exists }
  1122.                           If Objects[A].Pos=CurrentRoom Then
  1123.                           Begin
  1124.                                { The object is in the current room }
  1125.                                { Get the object }
  1126.                                Objects[A].Pos:=0;
  1127.                                TextColor(LightCyan);
  1128.                                WriteLn;
  1129.                                WriteLn('You get the ',O);
  1130.                                WriteLn;
  1131.                           End
  1132.                           Else
  1133.                           Begin
  1134.                                If Objects[A].Pos=0 Then
  1135.                                Begin
  1136.                                     { The object is already in the possession }
  1137.                                     { of the player...                        }
  1138.                                     TextColor(LightCyan);
  1139.                                     WriteLn;
  1140.                                     WriteLn('You already have the ',O);
  1141.                                     WriteLn;
  1142.                                End
  1143.                                Else
  1144.                                Begin
  1145.                                     WriteLn('You don''t see the ',O);
  1146.                                     WriteLn;
  1147.                                End;
  1148.                           End;
  1149.                      End
  1150.                      Else
  1151.                      Begin
  1152.                           { The object doesn't exist }
  1153.                           WriteLn;
  1154.                           TextColor(LightRed);
  1155.                           Writeln('What are you talking about ?');
  1156.                           Writeln;
  1157.                      End;
  1158.                 End;
  1159.  
  1160.     As usual, you have to put a call to this procedure in the Play procedure...
  1161.   This command has the same problem as the examine command, so we must do the
  1162.   same thing that you did in the examine command: join the parsed array in
  1163.   a string that has the name of the object:
  1164.  
  1165.                 If Parsed[1]='GET' Then
  1166.                 Begin
  1167.                      Valid:=True;
  1168.                      { Join the words again }
  1169.                      D:=3;
  1170.                      E:=Parsed[2];
  1171.                      While Parsed[D]<>'' Do
  1172.                      Begin
  1173.                           E:=E+' '+Parsed[D];
  1174.                           Inc(D);
  1175.                      End;
  1176.                      Get(E);
  1177.                 End;
  1178.  
  1179.     The drop procedure is similar, altough is a bit simpler:
  1180.  
  1181.                 Procedure Drop(O:String);
  1182.                 Var A:Byte;
  1183.                     Flag:Boolean;
  1184.                 Begin
  1185.                      Flag:=False;
  1186.                      A:=0;
  1187.                      Repeat
  1188.                            Inc(A);
  1189.                            If (O=Upper(Objects[A].Name)) And
  1190.                               (Objects[A].Pos=0) Then Flag:=True;
  1191.                      Until (A>NumberObjs) Or (Flag=True);
  1192.                      If Flag=True Then
  1193.                      Begin
  1194.                           { The object is in the player's possession }
  1195.                           Objects[A].Pos:=CurrentRoom;
  1196.                           TextColor(LightCyan);
  1197.                           WriteLn;
  1198.                           WriteLn('You drop the ',O);
  1199.                           WriteLn;
  1200.                      End
  1201.                      Else
  1202.                      Begin
  1203.                           { The object doesn't exist or it isn't in the }
  1204.                           { player's possession... }
  1205.                           TextColor(LightRed);
  1206.                           WriteLn;
  1207.                           WriteLn('You don''t have the ',O);
  1208.                           WriteLn;
  1209.                      End;
  1210.                 End;
  1211.  
  1212.     Again, add the following code to the Play procedure... You already know
  1213.   what it does:
  1214.  
  1215.                 If Parsed[1]='DROP' Then
  1216.                 Begin
  1217.                      Valid:=True;
  1218.                      { Join the words again }
  1219.                      D:=3;
  1220.                      E:=Parsed[2];
  1221.                      While Parsed[D]<>'' Do
  1222.                      Begin
  1223.                           E:=E+' '+Parsed[D];
  1224.                           Inc(D);
  1225.                      End;
  1226.                      Drop(E);
  1227.                 End;
  1228.  
  1229.     So, Get and Drop are ready... Now, let's go to the third 'class' of
  1230.   object manipulation commands: The use commands. The use commands are lots
  1231.   of commands, but they all do the same thing: Manipulate the object. For
  1232.   example... You can use the Gas Mask by typing USE MASK... But let's do things
  1233.   harder... For example, USE MASK can be thought as breaking it! You are
  1234.   using it... So, let's do the WEAR command... :))) It is harder that way for
  1235.   the player know what to do... The uses commands are just lines and lines of
  1236.   IFs, because every object has a different effect. Some types of commands
  1237.   require more than one parameter. For example, to use the sword, you must type
  1238.   USE SWORD ON MONSTER...
  1239.     In FangLore we have two use commands... The USE command and the WEAR
  1240.   command. The wear command is only used for the gas mask... The use command
  1241.   is for the others. The theory is simple... According to the object you
  1242.   select, you must do a block of instruction that define what happens.
  1243.   For example, when you use the shovel with the door in room 20, the door
  1244.   to FangLore opens... Code for the WEAR command... This is so small and
  1245.   simple that we'll include it in the Play procedure.
  1246.  
  1247.                     If Parsed[1]='WEAR' Then
  1248.                     Begin
  1249.                          { Join the words again }
  1250.                          D:=3;
  1251.                          E:=Parsed[2];
  1252.                          While Parsed[D]<>'' Do
  1253.                          Begin
  1254.                               E:=E+' '+Parsed[D];
  1255.                               Inc(D);
  1256.                          End;
  1257.                          If E='GAS MASK' Then
  1258.                          Begin
  1259.                               Valid:=True;
  1260.                               { Check if the player has the gas mask     }
  1261.                               { We know that the mask is object number 2 }
  1262.                               If Objects[2].Pos=0 Then
  1263.                               Begin
  1264.                                    Mask:=True;
  1265.                                    TextColor(LightCyan);
  1266.                                    WriteLn;
  1267.                                    WriteLn('You wear the gas mask...');
  1268.                                    WriteLn;
  1269.                               End
  1270.                               Else
  1271.                               Begin
  1272.                                    TextColor(LightRed);
  1273.                                    WriteLn;
  1274.                                    WriteLn('You don''t have the gas mask.');
  1275.                                    WriteLn;
  1276.                               End;
  1277.                          End;
  1278.                     End;
  1279.  
  1280.     The Mask variable is a global variable of type Boolean. It should be
  1281.   initialized to False in the Init procedure. If it is true, that means that
  1282.   we are using it. We'll see later how that can be used...
  1283.  
  1284.     Now, let's move on to the USE command... There are the following
  1285.   possibilities:
  1286.  
  1287.                 USE SHOVEL ON DOOR
  1288.                 USE SWORD ON MONSTER
  1289.  
  1290.     The parser should eliminate the ON prenom... Do that with the EliminPrenoms
  1291.   procedure (see details in the first article of this serie).
  1292.     So, let's do the USE procedure:
  1293.  
  1294.                  Procedure Use(Arg:ParsedType);
  1295.                  Var A:Byte;
  1296.                      Flag:Boolean;
  1297.                  Begin
  1298.                       Flag:=False;
  1299.                       A:=0;
  1300.                       Repeat
  1301.                             Inc(A);
  1302.                             If (Arg[2]=Upper(Objects[A].Name)) And
  1303.                                (Objects[A].Pos=0) Then Flag:=True;
  1304.                       Until (A>NumberObjs) Or (Flag=True);
  1305.                       If Flag=True Then
  1306.                       Begin
  1307.                            { The object is "usable" }
  1308.                            Flag:=False;
  1309.                            { Check if player want to use the shovel }
  1310.                            If Arg[2]='SHOVEL' Then
  1311.                            Begin
  1312.                                 Flag:=True;
  1313.                                 { Check if it is used with the door }
  1314.                                 If Arg[3]='DOOR' Then
  1315.                                 Begin
  1316.                                      { Check if the player is in the proper }
  1317.                                      { location...                          }
  1318.                                      If CurrentRoom=20 Then
  1319.                                      Begin
  1320.                                           { The player is in the right place }
  1321.                                           { Open passage between the garden  }
  1322.                                           { and the mansion...               }
  1323.                                           Rooms[20].North:=15;
  1324.                                           TextColor(LightCyan);
  1325.                                           WriteLn;
  1326.                                           WriteLn('You knock the door down...');
  1327.                                           WriteLn;
  1328.                                      End
  1329.                                      Else
  1330.                                      Begin
  1331.                                           { The player is someplace else }
  1332.                                           TextColor(LightRed);
  1333.                                           WriteLn;
  1334.                                           WriteLn('I don''t see a door...');
  1335.                                           WriteLn;
  1336.                                      End;
  1337.                                 End
  1338.                                 Else
  1339.                                 Begin
  1340.                                      { The player didn't used the shovel with }
  1341.                                      { the door...                            }
  1342.                                      TextColor(LightRed);
  1343.                                      WriteLn;
  1344.                                      WriteLn('Use it with what ?!');
  1345.                                      WriteLn;
  1346.                                 End;
  1347.                            End;
  1348.                            If Arg[2]='SWORD' Then
  1349.                            Begin
  1350.                                 Flag:=True;
  1351.                                 { I'll include the code here in a future  }
  1352.                                 { issue, when I'll talk about monsters... }
  1353.                            End;
  1354.                       End;
  1355.                       If Flag=False Then
  1356.                       Begin
  1357.                            { No "legal" objects were used }
  1358.                            TextColor(LightRed);
  1359.                            WriteLn;
  1360.                            WriteLn('Can't use that...');
  1361.                            WriteLn;
  1362.                       End;
  1363.                  End;
  1364.  
  1365.     We've must passe the entire parsed array as a parameter to the procedure,
  1366.   because objects like the shovel and the sword need another 'object' to work
  1367.   with... Remember that you are not limited to USE objects... You can use,
  1368.   for instance, a lever... It is not an object because you can't carry it
  1369.   around, but you can use it... But the code must be changed slightly... In
  1370.   the case of FangLore, we can only use objects... :)
  1371.     Notice also that in the case of the USE command, we can't use the trick
  1372.   we did with the Examine, Get, Drop and Wear commands, the trick of joining
  1373.   the parsed array together to form the name of the objects with more than
  1374.   one word (i.e. GAS MASK)... So, it is good to use as the name of the objects
  1375.   single words, or else lot's of manipulations are needed... In the case of
  1376.   FangLore there isn't a problem, but in other games it might be...
  1377.     Again, you must add the following code to the Play procedure:
  1378.  
  1379.                     If Parsed[1]='USE' Then
  1380.                     Begin
  1381.                          Valid:=True;
  1382.                          Use(Parsed);
  1383.                     End;
  1384.  
  1385.     So, this article is finished... Next article of this serie will cover
  1386.   monsters and special rooms... Don't miss it !! :)))
  1387.  
  1388.  
  1389. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  1390.  
  1391.  
  1392.   5. The wonderfull world of scrolies !
  1393.  
  1394.     5.1. Introduction
  1395.  
  1396.     Welcome to another article by your good (??) friend Spellcaster...
  1397.     This one is about scrollers... Or scrollies... :)
  1398.     So, what are they ?
  1399.     Scrollers are simply a piece of text that scrolls by the screen... Almost
  1400.   every demo in the world has at least one... There are billions of types of
  1401.   scrollers. They all revolve around the same principle... Memory...
  1402.     Scrollers are simple alterations of memory... If you move the memory the
  1403.   right way, you achieve the effect...
  1404.     Graphical scrollers are very similar to text scrollers... The only
  1405.   difference is that in text scrollers the text appears a character at a time,
  1406.   and in graphical scrollers, the text appears one collumn at a time... For
  1407.   example, if the font you are using is 16 pixels width, a character appears
  1408.   after 16 calls to the procedure that scrolls... Got it ?
  1409.  
  1410.     This article makes heavy use of the Font unit that is supplied with this
  1411.   issue... All the theory behind it was explained in last issue. Read the
  1412.   source code for more details... I've also included a color font,but is
  1413.   fairly limited... I hate drawing fonts, so I used one I've made for an
  1414.   old game of mine.
  1415.     Just remember one thing about the routine... Because of a small error in
  1416.   the conception of the font, the position in the array is not denoted as
  1417.   (x,y) but (y,x)... That is, if you want to know the color of the first
  1418.   pixel of the second line of character 'A', and store that value in variable
  1419.   C, you'll have to do:
  1420.  
  1421.                     C:=Font^['A',1,2]
  1422.  
  1423.     Sorry about the confusing explanation...
  1424.  
  1425.     There is one routine that will be used in some of the example programs...
  1426.   It is a procedure that draws a column of a character in the right edge of
  1427.   the screen... All the scrollers in the examples scroll from right to left,
  1428.   because it's easier to read that way. So, here's the code for that routine:
  1429.  
  1430.                 Procedure PutVertLine(C:Char;Index,Y:Byte;Where:Word);
  1431.                 Var B:Byte;
  1432.                 Begin
  1433.                      For B:=1 To 16 Do
  1434.                        PutPixel(319,Y+B,Font^[C,B,Index],Where);
  1435.                 End;
  1436.  
  1437.     Y is the line in which the procedure starts to draw the character...
  1438.     Also, all the scrollers start in the middle of the screen...
  1439.     So let's do it...
  1440.  
  1441.     5.2. Simple scroller
  1442.  
  1443.     The simple scroller is so simple I shouldn't waste time describing it... :)
  1444.     The ideia is just this: You write part of a character (with the PutVertLine
  1445.   procedure given above) and you scroll the screen to the left. Code:
  1446.  
  1447.                 Program Simple_Scroller;
  1448.  
  1449.                 Uses Mode13h,CFont,Crt;
  1450.  
  1451.                 Var Colors:RgbList;
  1452.                     Txt:String;
  1453.                     Ch:Byte;
  1454.                     A:Byte;
  1455.  
  1456.                 Procedure PutVertLine(C:Char;Index,Y:Byte;Where:Word);
  1457.                 Var B:Byte;
  1458.                 Begin
  1459.                      For B:=1 To 16 Do
  1460.                        PutPixel(319,Y+B,Font^[C,B,Index],Where);
  1461.                 End;
  1462.  
  1463.                 Procedure ScrollLeft;
  1464.                 Var B:Byte;
  1465.                 Begin
  1466.                      WaitVbl;
  1467.                      For B:=90 To 110 Do
  1468.                      Move(Mem[VGA:(320*B+1)],Mem[VGA:(320*B)],319);
  1469.                 End;
  1470.  
  1471.                 Begin
  1472.                      { Initialization }
  1473.                      InitGraph;
  1474.                      LoadFont('Font.Fnt');
  1475.                      LoadPal('Font.Pal',Colors);
  1476.                      SetPalette(Colors);
  1477.                      Txt:='SPELLCASTER CODED THIS  ';
  1478.                      Txt:=Txt+'THIS SUCKS  ';
  1479.                      Txt:=Txt+'SO, SPELLCASTER SUCKS !!';
  1480.                      Txt:=Txt+'      ';
  1481.                      Ch:=1;
  1482.                      { Scrolling }
  1483.                      Repeat
  1484.                            For A:=1 To 16 Do
  1485.                            Begin
  1486.                                 PutVertLine(Txt[Ch],A,92,VGA);
  1487.                                 ScrollLeft;
  1488.                            End;
  1489.                            Inc(Ch);
  1490.                            If Ch>Length(Txt) Then Ch:=1;
  1491.                      Until Keypressed;
  1492.                      { Shutting down }
  1493.                      CloseGraph;
  1494.                 End.
  1495.  
  1496.     This is simple... The ScrollLeft procedure was taught in the graphics
  1497.   section of issue 7 of 'The Mag'... The only difference is that this procedure
  1498.   only scrolls lines 90 through 110... It does that to speed up the program.
  1499.   This is a fairly quick program, so we didn't use any virtual screens.
  1500.   This should be easy to understand. We write do the screen the column indexed
  1501.   by variable A, then we increment it (the FOR cicle), then we scroll the
  1502.   screen to the left. We do this 16 times, and then we go on to the next
  1503.   character... When we finish with the string, we start all over again...
  1504.  
  1505.     5.3. Sine scroller
  1506.  
  1507.     This is when the thing becomes tougher... What's a sine scroller ?
  1508.     It's a scroller than goes up and down, like a sine wave... If you don't
  1509.   know what a sine wave is, go to a 8th grade book... It should come there.
  1510.     There are lot's of ways to do this, and the one I'm thinking about is a
  1511.   little bit complicated. I'll draw a bit of ASCII:
  1512.  
  1513.      |
  1514.      |AAA                BBBBBB                CCCCCC                D....
  1515.      |   AA            BB      BB            CC      CD            DD ....
  1516.      |     A          B          B          C          D          D   ....
  1517.      |      AA      AA            BB      CC            DD      DD    ....
  1518.      |        AAAAAA                BBCCCC                DDDDDD      ....
  1519.      |
  1520.  
  1521.      ^ That is the left edge of the screen. The 'A' character represents a
  1522.        character's pixels, the 'B' character represents another character's
  1523.        pixels, etc...
  1524.        Only the top line of a character is shown, for simplicity stake...
  1525.        That is the first frame of movement... Let's see the second and third
  1526.        frame of movement:
  1527.  
  1528.      |
  1529.      |AAA                BBBBBB                CCCCCC                E....
  1530.      |   AA            BB      BB            CC      DD            DD ....
  1531.      |     A          B          B          C          D          D   ....
  1532.      |      AA      AB            BB      CC            DD      DD    ....
  1533.      |        AAAAAA                BCCCCC                DDDDDD      ....
  1534.      |
  1535.  
  1536.      |
  1537.      |AAA                BBBBBB                CCCCCD                E....
  1538.      |   AA            BB      BB            CC      DD            DE ....
  1539.      |     A          B          B          C          D          D   ....
  1540.      |      AA      BB            BB      CC            DD      DD    ....
  1541.      |        AAAAAA                CCCCCC                DDDDDD      ....
  1542.      |
  1543.  
  1544.     Notice that the absolute Y values are the same for every X in the screen,
  1545.   independent of the character that occupies that position...
  1546.     So, the ideia is to draw a part of the string in the screen, from X=0 to
  1547.   X=319, changing the Y according to a sine calculation, that is a function
  1548.   of X... This is harder than it looks. For example, in the first frame we
  1549.   have the 'plotting' of the string starting in character 'A' (presumably the
  1550.   first character of the string). Well, in the second frame, it also starts
  1551.   with character 'A'... So, what's the difference ?
  1552.     Well, in the second frame, the 'plotting' starts in the character's
  1553.   second column, and in the third frame,  it starts on the third column, and
  1554.   so forth, until it arrives to the 16th column... There it switches to the
  1555.   first column of the next character in the string...
  1556.     The code looks awfully messy, but it works fine, and if you look carefully
  1557.   at it, it will be as clear as a day in the Carabeean... :)
  1558.     Notice that there is no need now for the ScrollLeft procedure, and that
  1559.   the PutVirtLine procedure was changed in order to enable the us to specify
  1560.   in which X coordinate to draw the column...
  1561.  
  1562.                 Program Sine_Scroller;
  1563.  
  1564.                 Uses Mode13h,CFont,Crt;
  1565.  
  1566.                 Var Colors:RgbList;
  1567.                     Txt:String;
  1568.                     Ch,Ch1:Byte;
  1569.                     A,Y,Index:Byte;
  1570.                     X:Integer;
  1571.  
  1572.                 Procedure PutVertLine(C:Char;Index,X,Y,Where:Word);
  1573.                 Var B:Byte;
  1574.                 Begin
  1575.                      For B:=1 To 16 Do
  1576.                        PutPixel(X,Y+B,Font^[C,B,Index],Where);
  1577.                 End;
  1578.  
  1579.                 Begin
  1580.                      { Initialization }
  1581.                      InitGraph;
  1582.                      InitTables;
  1583.                      LoadFont('Font.Fnt');
  1584.                      LoadPal('Font.Pal',Colors);
  1585.                      SetPalette(Colors);
  1586.                      Txt:='SPELLCASTER CODED THIS  ';
  1587.                      Txt:=Txt+'THIS SUCKS  ';
  1588.                      Txt:=Txt+'SO, SPELLCASTER SUCKS !!';
  1589.                      Txt:=Txt+'      ';
  1590.                      Ch:=1;
  1591.                      { Scrolling }
  1592.                      Repeat
  1593.                            For A:=1 To 16 Do
  1594.                            Begin
  1595.                                 Index:=A;
  1596.                                 X:=0;
  1597.                                 Ch1:=Ch;
  1598.                                 WaitVbl;
  1599.                                 Repeat
  1600.                                       Repeat
  1601.                                             Y:=100+Trunc(20*Sines^[X]);
  1602.                                             PutVertLine(Txt[Ch1],Index,X,Y,VGA);
  1603.                                             Inc(Index);
  1604.                                             Inc(X);
  1605.                                       Until (Index>16) Or (X>319);
  1606.                                       Index:=1;
  1607.                                       Inc(Ch1);
  1608.                                       If Ch1>Length(Txt) Then Ch1:=0;
  1609.                                 Until X>319;
  1610.                            End;
  1611.                            Inc(Ch);
  1612.                      Until Keypressed;
  1613.                      { Shutting down }
  1614.                      CloseGraph;
  1615.                 End.
  1616.  
  1617.     This looks bad, but after some days looking at it, it will be simple to
  1618.   understand... :) Any doubts, mail me... :)
  1619.     This could be speeded up a bit by using a precalculated table for the Y
  1620.   values... We are already using a pregenerated array for the sines. This
  1621.   could also be speeded up by using assembler... But I think you can manage
  1622.   that, if you really want... The next issue, I'll probably put more ASM code
  1623.   in the graphics section...
  1624.  
  1625.     5.4. Mirror and water scroller
  1626.  
  1627.     Well, these two are fairly easy... Why ? Because they are effects upon
  1628.   the scrollers... So, you can have a sine-water scroller... Or a normal-water
  1629.   scroller. For the examples, I'll use the simple scroller as a basis...
  1630.  
  1631.     First, let's do the mirror scroller... That's the simpler one... The ideia
  1632.   is to flip what is scrolling around the X axis, that is, draw the image
  1633.   upside down... You could to this by drawing the scroller again, but this time
  1634.   with Y coordinates flipped... But that would be too slow to combine with a
  1635.   sine scroller... So, what I do is to grab the image that is already drawn
  1636.   and copy it below, but inverted... To do so, here's the DoMirror procedure:
  1637.  
  1638.                 Procedure DoMirror;
  1639.                 Var B:Byte;
  1640.                 Begin
  1641.                      WaitVbl;
  1642.                      For B:=0 To 20 Do
  1643.                      Move(Mem[VGA:(320*(90+B))],Mem[VGA:(320*(130-B))],320);
  1644.                 End;
  1645.  
  1646.     It copies the 20 lines ranging from 90 to 110 (where the original scroller
  1647.   is) and puts it on the 20 lines ranging from 130 to 110 (respectivelly).
  1648.     You can addapt this procedure to mirror any effect... A nice touch is to
  1649.   dim a bit the inverted image, but I'll leave that to a future issue...
  1650.     Don't forget to call this procedure after you draw every frame. Here is a
  1651.   complete example:
  1652.  
  1653.                 Program Mirror_Scroller;
  1654.  
  1655.                 Uses Mode13h,CFont,Crt;
  1656.  
  1657.                 Var Colors:RgbList;
  1658.                     Txt:String;
  1659.                     Ch:Byte;
  1660.                     A:Byte;
  1661.  
  1662.                 Procedure PutVertLine(C:Char;Index,Y:Byte;Where:Word);
  1663.                 Var B:Byte;
  1664.                 Begin
  1665.                      For B:=1 To 16 Do
  1666.                        PutPixel(319,Y+B,Font^[C,B,Index],Where);
  1667.                 End;
  1668.  
  1669.                 Procedure ScrollLeft;
  1670.                 Var B:Byte;
  1671.                 Begin
  1672.                      WaitVbl;
  1673.                      For B:=90 To 110 Do
  1674.                      Move(Mem[VGA:(320*B+1)],Mem[VGA:(320*B)],319);
  1675.                 End;
  1676.  
  1677.                 Procedure DoMirror;
  1678.                 Var B:Byte;
  1679.                 Begin
  1680.                      WaitVbl;
  1681.                      For B:=0 To 20 Do
  1682.                      Move(Mem[VGA:(320*(90+B))],Mem[VGA:(320*(130-B))],320);
  1683.                 End;
  1684.  
  1685.                 Begin
  1686.                      { Initialization }
  1687.                      InitGraph;
  1688.                      LoadFont('Font.Fnt');
  1689.                      LoadPal('Font.Pal',Colors);
  1690.                      SetPalette(Colors);
  1691.                      Txt:='SPELLCASTER CODED THIS  ';
  1692.                      Txt:=Txt+'THIS SUCKS  ';
  1693.                      Txt:=Txt+'SO, SPELLCASTER SUCKS !!';
  1694.                      Txt:=Txt+'      ';
  1695.                      Ch:=1;
  1696.                      { Scrolling }
  1697.                      Repeat
  1698.                            For A:=1 To 16 Do
  1699.                            Begin
  1700.                                 PutVertLine(Txt[Ch],A,92,VGA);
  1701.                                 DoMirror;
  1702.                                 ScrollLeft;
  1703.                            End;
  1704.                            Inc(Ch);
  1705.                            If Ch>Length(Txt) Then Ch:=1;
  1706.                      Until Keypressed;
  1707.                      { Shutting down }
  1708.                      CloseGraph;
  1709.                 End.
  1710.  
  1711.     So, here's for the mirror scroller... Now, let's move to the water
  1712.   scroller... This is a variation of the mirror scroller... The only diference
  1713.   is that the water scroller has some 'ripples' in the inverted mirror...
  1714.   How can we do that ? Simple... Instead of just copying the scroller image to
  1715.   other part of the screen, we shake it a bit, by using a random value...
  1716.     So, the DoMirror procedure is replaced by teh DoWater procedure:
  1717.  
  1718.                 Procedure DoWater;
  1719.                 Var B:Byte;
  1720.                 Begin
  1721.                      WaitVbl;
  1722.                      For B:=0 To 20 Do
  1723.                      Move(Mem[VGA:(320*(90+B))],
  1724.                           Mem[VGA:(320*(130-B)+Random(3))],316);
  1725.                 End;
  1726.  
  1727.     The rest remains the same... Look at the complete program:
  1728.  
  1729.                 Program Water_Scroller;
  1730.  
  1731.                 Uses Mode13h,CFont,Crt;
  1732.  
  1733.                 Var Colors:RgbList;
  1734.                     Txt:String;
  1735.                     Ch:Byte;
  1736.                     A:Byte;
  1737.  
  1738.                 Procedure PutVertLine(C:Char;Index,Y:Byte;Where:Word);
  1739.                 Var B:Byte;
  1740.                 Begin
  1741.                      For B:=1 To 16 Do
  1742.                        PutPixel(319,Y+B,Font^[C,B,Index],Where);
  1743.                 End;
  1744.  
  1745.                 Procedure ScrollLeft;
  1746.                 Var B:Byte;
  1747.                 Begin
  1748.                      WaitVbl;
  1749.                      For B:=90 To 110 Do
  1750.                      Move(Mem[VGA:(320*B+1)],Mem[VGA:(320*B)],319);
  1751.                 End;
  1752.  
  1753.                 Procedure DoWater;
  1754.                 Var B:Byte;
  1755.                 Begin
  1756.                      WaitVbl;
  1757.                      For B:=0 To 20 Do
  1758.                      Move(Mem[VGA:(320*(90+B))],
  1759.                           Mem[VGA:(320*(130-B)+Random(3))],316);
  1760.                 End;
  1761.  
  1762.                 Begin
  1763.                      { Initialization }
  1764.                      InitGraph;
  1765.                      LoadFont('Font.Fnt');
  1766.                      LoadPal('Font.Pal',Colors);
  1767.                      SetPalette(Colors);
  1768.                      Txt:='SPELLCASTER CODED THIS  ';
  1769.                      Txt:=Txt+'THIS SUCKS  ';
  1770.                      Txt:=Txt+'SO, SPELLCASTER SUCKS !!';
  1771.                      Txt:=Txt+'      ';
  1772.                      Ch:=1;
  1773.                      { Scrolling }
  1774.                      Repeat
  1775.                            For A:=1 To 16 Do
  1776.                            Begin
  1777.                                 PutVertLine(Txt[Ch],A,92,VGA);
  1778.                                 DoWater;
  1779.                                 ScrollLeft;
  1780.                            End;
  1781.                            Inc(Ch);
  1782.                            If Ch>Length(Txt) Then Ch:=1;
  1783.                      Until Keypressed;
  1784.                      { Shutting down }
  1785.                      CloseGraph;
  1786.                 End.
  1787.  
  1788.     Instead of using random values, you can use some sort of pregenerated
  1789.   table or sine wave to give a much smoother look...
  1790.     Ok, this article is finally finished... :)
  1791.     Scrollers are a pain in the ass in a lot of demos, but if you add some
  1792.   cool effects, you can make them cool things... So try to do a 3d-texture-
  1793.   -and-bump-mapped-phong-shaded-faster-than-light scroller... :)))
  1794.  
  1795.  
  1796. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  1797.  
  1798.  
  1799.   6. First steps into virtual reality
  1800.  
  1801.     Welcome to the first article about 3d on 'The Mag'... Nowadays, everyone
  1802.   is excited with virtual reality and other stuff like that. Everytime I
  1803.   read the rec.games.programmer, I read tons of articles that say 'I wanna make
  1804.   a DOOM like game'... And in comp.sys.ibm.pc.demos, I read articles that say
  1805.   'I wanna do a texture-mapped phong-shaded duck rotating in a voxel
  1806.   landscape !'... So, 3d is everywhere ! But let's move on...
  1807.  
  1808.     Let's think about the principle behind 3d. The ideia is to have a model of
  1809.   some kind and convert to an image. A model is a set of data that describe the
  1810.   virtual 'world'.
  1811.     As everyone knows, 3d stands for three-dimensions... But the computer screen
  1812.   only has 2 dimensions, so, every object in the computer can and is identified
  1813.   with 2 coordinates, X and Y, while everything in the real is described with
  1814.   3 coordinates, X, Y and Z... It's fairly easy to estabilish the relation
  1815.   Z/Depth... But that's not always true... You can make Y your depth, or even
  1816.   an arbitrary axis... But it is convencioned that X is horizontal, Y is
  1817.   vertical and Z is the depth... So let's play with that...
  1818.     So, the main problem with 3d is to transform (x,y,z) points, in (x,y)
  1819.   points... So, how can we do that ?
  1820.     It's easier than you think... As you know the further away is an object,
  1821.   the closer to the horizon's center it is... So, the greater the Z coordinate,
  1822.   the closer it will be to the center of the screen. If we make all the
  1823.   coordinates relative to the center of the screen, the bigger the Z, the
  1824.   smaller will be X and Y... What's the operation that does this ? Division...
  1825.   So, given the (X,Y,Z) that describe a point in 3d space, the transformed
  1826.   coordinates (Xt,Yt) will be:
  1827.  
  1828.                  Xt= X*256                     Yt= Y*256
  1829.                      -----                         -----
  1830.                        Z                             Z
  1831.  
  1832.     Why is that multiplication by 256 there ? Well, that's the camera factor.
  1833.   If we don't multiply, all would look pretty ugly, because the number would
  1834.   be too small... Why did I choose 256 ? For two reasons... First, that number
  1835.   looks good (if you use other numbers, you can even get a fish-eyed lens
  1836.   effect). Second, multiplication by 256 can be achieve by a lightning-fast
  1837.   shift-left operation, if we shift 8 bits... So, the procedure that converts
  1838.   3 dimensions in 2 dimensions is the following:
  1839.  
  1840.                Procedure Conv3d(X,Y,Z:Real;Var Xt,Yt:Integer);
  1841.                Begin
  1842.                     Xt:=160+Trunc((X*256/Z));
  1843.                     Yt:=100+Trunc((Y*256/Z));
  1844.                End;
  1845.  
  1846.     We use reals for coordinates because of precision of rotations...
  1847.     There are problem with this routine if the Z is 0... But you don't need
  1848.   to compute points that have the Z equal to 0, because they are on top of
  1849.   the camera. Don't forget that the X,Y and Z are relative to the center of
  1850.   the virtual 'world', that is, relative to the camera. That 160 and 100 on
  1851.   the procedure are the center of the screen.
  1852.     When you plot a point in the screen, don't forget to check if the point
  1853.   lies inside the screen! I forgot that when I was coding an example program
  1854.   and it was giving me an headache... Here is a routine that plots a 3d point
  1855.   on the screen:
  1856.  
  1857.                Procedure Plot3d(X,Y,Z:Real;Color:Byte;Where:Word);
  1858.                Var Xt,Yt:Integer;
  1859.                Begin
  1860.                     Xt:=160+Trunc(X*256/Z);
  1861.                     If (Xt<0) Or (Xt>319) Then Exit;
  1862.                     Xt:=100+Trunc(Y*256/Z);
  1863.                     If (Yt<0) Or (Yt>199) Then Exit;
  1864.                     Mem[Where:(320*Yt+Xt)]:=Color;
  1865.                End;
  1866.  
  1867.     Exit is a nifty command that makes Pascal exit the block it is in... If
  1868.   you do Exit inside a function or a procedure, the program goes back to where
  1869.   that function or procedure was called... If you do Exit in the main program,
  1870.   the program will end imediately. Why do we calculate the Xt and check if
  1871.   it is in the screen ? Because it is faster, if the point is outside the
  1872.   screen... If the point is outside the screen, there's no use for the Yt
  1873.   coordinate...
  1874.     Now that we've already drawn a 3d point, we can move on to the next
  1875.   section...
  1876.  
  1877.     6.1. Wireframe graphics
  1878.  
  1879.     What are wireframe graphics ?
  1880.     Well, wireframe graphics are a kind of 3d graphics only made of points
  1881.   and lines connecting points, without any kind of shading, texturing or
  1882.   hidden-surface removal...
  1883.     So, to do wireframe graphics, we need a kind of structure that allows us
  1884.   to store the point's data and the information of what points are connect to
  1885.   each others... In our example, we'll use a static structure, but you could
  1886.   use a dinamic one... First, let's define the maximum number of points and
  1887.   lines an object can have:
  1888.  
  1889.                       Const MaxPoints=30;
  1890.                             MaxLines=30;
  1891.  
  1892.     Now, let's define the structures necessary:
  1893.  
  1894.                    Type Point3d=Record
  1895.                                       X,Y,Z:Real;
  1896.                                 End;
  1897.  
  1898.                         Object3d=Record
  1899.                                        NumberPoints:Byte;
  1900.                                        NumberLines:Byte;
  1901.                                        Pt:Array[1..MaxPoints] Of Point3d;
  1902.                                        Lines:Array[1..MaxLines,1..2] Of Byte;
  1903.                                  End;
  1904.  
  1905.     So, now we need to create an object:
  1906.  
  1907.                        Var Car:Object3d;
  1908.  
  1909.     And afterwards, we need to load some data into the object... To do so, I
  1910.   made a small utility that enables you to type in 3d coordinates and to type
  1911.   which points connect, and store that data in a file. Then, you can read it
  1912.   back to Object3d variable with the LoadData procedure... The code for that
  1913.   procedure is below... The utility includes the source, so that you can change
  1914.   it anyway you see fit. The program is called 3DGEN.PAS. Notice that the
  1915.   program only uses integer numbers, integer's that are loaded into reals.
  1916.  
  1917.             Procedure Load3d(Filename:String;Var Obj:Object3d);
  1918.             Var F:Text;
  1919.                 A:Byte;
  1920.             Begin
  1921.                  Assign(F,Filename);
  1922.                  Reset(F);
  1923.                  ReadLn(F,Obj.NumberPoints);
  1924.                  ReadLn(F,Obj.NumberLines);
  1925.                  For A:=1 To Obj.NumberPoints Do
  1926.                    ReadLn(F,Obj.Pt[A].X,Obj.Pt[A].Y,Obj.Pt[A].Z);
  1927.                  For A:=1 To Obj.NumberLines Do
  1928.                    ReadLn(F,Obj.Lines[A,1],Obj.Lines[A,2]);
  1929.                  Close(F);
  1930.             End;
  1931.  
  1932.     Now, all we have to do is to join the points that are to be joined.
  1933.     Let's check out how this is done:
  1934.  
  1935.                Procedure Draw3d(Obj:Object3d;XOff,YOff,ZOff:Integer;
  1936.                                 Color:Byte;Where:Word);
  1937.                Var A:Byte;
  1938.                    Pt1,Pt2:Byte;
  1939.                    X1,Y1,X2,Y2:Integer;
  1940.                Begin
  1941.                     For A:=1 To Obj.NumberLines Do
  1942.                     Begin
  1943.                          Pt1:=Obj.Lines[A,1];
  1944.                          Pt2:=Obj.Lines[A,2];
  1945.                          Conv3d(Obj.Pt[Pt1].X+XOff,
  1946.                                 Obj.Pt[Pt1].Y+YOff,
  1947.                                 Obj.Pt[Pt1].Z+ZOff,
  1948.                                 X1,Y1);
  1949.                          Conv3d(Obj.Pt[Pt2].X+XOff,
  1950.                                 Obj.Pt[Pt2].Y+YOff,
  1951.                                 Obj.Pt[Pt2].Z+ZOff,
  1952.                                 X2,Y2);
  1953.                          LineC(X1,Y1,X2,Y2,Color,Where);
  1954.                     End;
  1955.                End;
  1956.  
  1957.     So, what's the deal here ? Well, simple... You have two lists in a
  1958.   structure. One has the coordinates of all the points in the object, and the
  1959.   other has the list the lines there are, stored by number of the point. For
  1960.   example, if
  1961.                 Ln[1,1]:=1;
  1962.                 Ln[1,2]:=5;
  1963.  
  1964.     That would mean that point 1 would connect to point 5... So, we traverse
  1965.   all the Lines array and connect the points to form the image... The XOff,
  1966.   YOff and ZOff variables are the offsets of the object... The object can be
  1967.   drawn in any region of 3d space. The LineC procedure that is called is a
  1968.   clipped version of the Line procedure I gave you in issue 4 of 'The Mag'.
  1969.   Check it's source in the Mode13h unit... The only diference between LineC
  1970.   and Line is that LineC checks if the point is within the borders of the
  1971.   screen.
  1972.     Let's see the complete example:
  1973.  
  1974.                Program WireFrame;
  1975.  
  1976.                Uses Mode13h,Crt;
  1977.  
  1978.                Const MaxPoints=30;
  1979.                      MaxLines=30;
  1980.  
  1981.                Type Point3d=Record
  1982.                                   X,Y,Z:Real;
  1983.                             End;
  1984.  
  1985.                     Object3d=Record
  1986.                                    NumberPoints:Byte;
  1987.                                    NumberLines:Byte;
  1988.                                    Pt:Array[1..MaxPoints] Of Point3d;
  1989.                                    Lines:Array[1..MaxLines,1..2] Of Byte;
  1990.                              End;
  1991.  
  1992.                Var A:Integer;
  1993.                    Car:Object3d;
  1994.                    D:Char;
  1995.  
  1996.                Procedure Conv3d(X,Y,Z:Real;Var Xt,Yt:Integer);
  1997.                Begin
  1998.                     Xt:=160+Trunc((X*256/Z));
  1999.                     Yt:=100+Trunc((Y*256/Z));
  2000.                End;
  2001.  
  2002.                Procedure Load3d(Filename:String;Var Obj:Object3d);
  2003.                Var F:Text;
  2004.                    A:Byte;
  2005.                Begin
  2006.                     Assign(F,Filename);
  2007.                     Reset(F);
  2008.                     ReadLn(F,Obj.NumberPoints);
  2009.                     ReadLn(F,Obj.NumberLines);
  2010.                     For A:=1 To Obj.NumberPoints Do
  2011.                       ReadLn(F,Obj.Pt[A].X,Obj.Pt[A].Y,Obj.Pt[A].Z);
  2012.                     For A:=1 To Obj.NumberLines Do
  2013.                       ReadLn(F,Obj.Lines[A,1],Obj.Lines[A,2]);
  2014.                     Close(F);
  2015.                End;
  2016.  
  2017.                Procedure Draw3d(Obj:Object3d;XOff,YOff,ZOff:Integer;
  2018.                                 Color:Byte;Where:Word);
  2019.                Var A:Byte;
  2020.                    Pt1,Pt2:Byte;
  2021.                    X1,Y1,X2,Y2:Integer;
  2022.                Begin
  2023.                     For A:=1 To Obj.NumberLines Do
  2024.                     Begin
  2025.                          Pt1:=Obj.Lines[A,1];
  2026.                          Pt2:=Obj.Lines[A,2];
  2027.                          Conv3d(Obj.Pt[Pt1].X+XOff,
  2028.                                 Obj.Pt[Pt1].Y+YOff,
  2029.                                 Obj.Pt[Pt1].Z+ZOff,
  2030.                                 X1,Y1);
  2031.                          Conv3d(Obj.Pt[Pt2].X+XOff,
  2032.                                 Obj.Pt[Pt2].Y+YOff,
  2033.                                 Obj.Pt[Pt2].Z+ZOff,
  2034.                                 X2,Y2);
  2035.                          LineC(X1,Y1,X2,Y2,Color,Where);
  2036.                     End;
  2037.                End;
  2038.  
  2039.                Begin
  2040.                     Initgraph;
  2041.                     Load3d('Car.3d',Car);
  2042.                     SetColor(1,63,63,0);
  2043.                     For A:=-300 To 300 Do
  2044.                     Begin
  2045.                     Draw3d(Car,-20,0,A,1,VGA);
  2046.                     Draw3d(Car,-20,0,A,0,VGA);
  2047.                     End;
  2048.                     D:=Readkey;
  2049.                     Closegraph;
  2050.                End.
  2051.  
  2052.     6.2. Basic transformations
  2053.  
  2054.     Everything in 3d should be done with matrixes, since they ease up a lot
  2055.   of calculations... I will explain latter why is this true.
  2056.     Matrixes are a very important part of algebra. So, I'll start this section
  2057.   by explaining basic matrix calculus. Imagine an 3x3 array and a 1x3 array:
  2058.  
  2059.                         ┌     ┐        ┌ ┐
  2060.                         │1 2 3│        │1│
  2061.                      A= │4 5 6│     B= │2│
  2062.                         │7 8 9│        │3│
  2063.                         └     ┘        └ ┘
  2064.  
  2065.     Matrixes are identified by a uppercase letter. B is also called a vector,
  2066.   and because it has 3 numbers, it is called a three-dimensional vector. Notice
  2067.   that you can think B as three coordinates to a point in 3d: X=1, Y=2 and Z=3.
  2068.     With this in mind, let's see matrix multiplication. Let's multiply A by
  2069.   B:
  2070.  
  2071.                           ┌     ┐ ┌ ┐ ┌           ┐ ┌  ┐
  2072.                           │1 2 3│ │1│ │1*1+2*2+3*3│ │14│
  2073.                      A*B= │4 5 6│*│2│=│4*1+5*2+6*3│=│32│
  2074.                           │7 8 9│ │3│ │7*1+8*2+9*3│ │50│
  2075.                           └     ┘ └ ┘ └           ┘ └  ┘
  2076.  
  2077.     This is an example, only... The rule is:
  2078.  
  2079.                           ┌     ┐ ┌ ┐ ┌        ┐
  2080.                           │a b c│ │x│ │ax+by+cz│
  2081.                      A*B= │d e f│*│y│=│dx+ey+fz│
  2082.                           │g h i│ │z│ │gx+hy+iz│
  2083.                           └     ┘ └ ┘ └        ┘
  2084.  
  2085.     You can multiply matrixes of any size, but we only need to know how to
  2086.   multiply a 3x3 matrix by a 1x3 matrix and a 4x4 matrix by a 1x4 matrix.
  2087.   There's just one rule to matrix multiplication: The number of columns of
  2088.   the first matrix must be equal to the number of lines of the second matrix.
  2089.     Here's the rule for 4x4 by 1x4 multiplication:
  2090.  
  2091.                           ┌       ┐ ┌ ┐ ┌           ┐
  2092.                           │a b c d│ │v│ │ax+by+cz+dv│
  2093.                      A*B= │e f g h│*│x│=│ex+fy+gz+hv│
  2094.                           │i j k l│ │y│ │ix+jy+kz+lv│
  2095.                           │m n o p│ │z│ │mx+ny+oz+pv│
  2096.                           └       ┘ └ ┘ └           ┘
  2097.  
  2098.     Other thing you should know is the identity matrix:
  2099.  
  2100.       3x3 identity matrix: ┌     ┐    4x4 identity matrix: ┌       ┐
  2101.                            │1 0 0│                         │1 0 0 0│
  2102.                            │0 1 0│                         │0 1 0 0│
  2103.                            │0 0 1│                         │0 0 1 0│
  2104.                            └     ┘                         │0 0 0 1│
  2105.                                                            └       ┘
  2106.  
  2107.     Plainly put, the identity matrix is a matrix with all the elements zero,
  2108.   except the elements in the main diagonal. The identity matrix has the property
  2109.   of not afecting the multiplication. So:
  2110.  
  2111.                          ┌     ┐ ┌ ┐ ┌           ┐ ┌ ┐
  2112.                          │1 0 0│ │5│ │1*5+0*7+0*8│ │5│
  2113.                          │0 1 0│*│7│=│0*5+1*7+0*8│=│7│
  2114.                          │0 0 1│ │8│ │0*5+0*7+1*8│ │8│
  2115.                          └     ┘ └ ┘ └           ┘ └ ┘
  2116.  
  2117.     Other thing that you need to know is multiplying two 3x3 matrixes and
  2118.   two 4x4 matrix:
  2119.  
  2120.                  ┌     ┐ ┌     ┐ ┌                          ┐
  2121.                  │a b c│ │j k l│ │aj+bm+cp ak+bn+cq al+bo+cr│
  2122.                  │d e f│*│m n o│=│dj+em+fp dk+en+fq dl+eo+fr│
  2123.                  │g h i│ │p q r│ │gj+hm+ip gk+hn+iq gl+ho+ir│
  2124.                  └     ┘ └     ┘ └                          ┘
  2125.  
  2126.     Multiplying two 4x4 matrixes is similar. The rules for multiplying
  2127.   matrixes of any size are the following:
  2128.  
  2129.            - The number of columns of the first matrix must be equal to
  2130.              the number of lines of the second matrix.
  2131.              Example: You want to multiply two matrixes, with dimensions
  2132.                       AxB and CxD (where A and C are the number of columns).
  2133.                       You can only multiply if A and D are equal.
  2134.            - The result matrix has the same number of columns of the second
  2135.              matrix and the same number of lines as the number of columns of
  2136.              the first matrix.
  2137.              Example: The result matrix of multiplying the above matrixes
  2138.                       would have C columns and A lines.
  2139.            - The item (item is one of the factors of the matrix) in position
  2140.              (x,y) of the matrix will be equal to the multiplication of line
  2141.              number Y with columns number X.
  2142.            - Matrix multiplication is NOT comutative, that is, you can not
  2143.              change the order of the operands !
  2144.  
  2145.     This is the basic stuff on matrixes... If you want to know more, go to
  2146.   any good algebra book... If I forgot something, I'll tell you in the
  2147.   explanation.
  2148.     Keep this in mind, because we'll use this later... Now, let's move on to
  2149.   the theory of the diferent transformations... Let's start on translation...
  2150.   Translation is the easiest operation of them all. Translating an object means
  2151.   moving it from it's current position to other position. To translate an
  2152.   object, you just have to add to it's coordinates a number. So, let's do a
  2153.   translation procedure:
  2154.  
  2155.               Procedure Translate(Var Obj:Object3d;XOff,YOff,ZOff:Integer);
  2156.               Var A:Byte;
  2157.               Begin
  2158.                    For A:=1 To Obj.NumberPoints Do
  2159.                    Begin
  2160.                         Obj.Pt[A].X:=Obj.Pt[A].X+XOff;
  2161.                         Obj.Pt[A].Y:=Obj.Pt[A].Y+YOff;
  2162.                         Obj.Pt[A].Z:=Obj.Pt[A].Z+ZOff;
  2163.                    End;
  2164.               End;
  2165.  
  2166.     Easy, isn't it ?
  2167.     Now, let's do something you'll think it's pretty useless, but that can have
  2168.   it's uses... Let's put the translation in a matrix form:
  2169.  
  2170.     The formula used for translation is as follows:
  2171.  
  2172.                      X = Xo+XOff
  2173.                      Y = Yo+YOff
  2174.                      Z = Zo+ZOff
  2175.  
  2176.     Where X, Y and Z are the translated coordinates; Xo, Yo and Zo are the
  2177.   original coordinates and XOff, YOff and ZOff are the offsets of the
  2178.   coordinates. Putting this in matrix form:
  2179.  
  2180.                ┌   ┐   ┌                 ┐┌  ┐   ┌       ┐
  2181.                │ X │   │ 1    0    0   0 ││Xo│   │Xo+XOff│
  2182.            P = │ Y │ = │ 0    1    0   0 ││Yo│ = │Yo+YOff│
  2183.                │ Z │   │ 0    0    1   0 ││Zo│   │Zo+ZOff│
  2184.                │ 1 │   │XOff YOff ZOff 1 ││ 1│   │   1   │
  2185.                └   ┘   └                 ┘└  ┘   └       ┘
  2186.  
  2187.     We'll see later the use of this... Don't forget you can discard that last
  2188.   one in the result column... :)
  2189.     So, let's move on to other basic transformation: Scaling ! Let's see a
  2190.   2d example... The 3d theory is equal.
  2191.     Scaling is the operation that transforms this:
  2192.  
  2193.     │                               │
  2194.     │                               │
  2195.     │                               │  +----+
  2196.     │  +--+             in this:    │  |    |
  2197.     │  |  |                         │  |    |
  2198.     │  +--+                         │  +----+
  2199.     └───────────────                └───────────────
  2200.  
  2201.     This is a scaling by two... To do so, we just have to multiply every point
  2202.   by two, right ?
  2203.     WRONG! If you multiply each point by two, all you'll get is a translation:
  2204.  
  2205.                 │                           │
  2206.                 │                           │
  2207.                 │                           │    +--+
  2208.                 │  +--+             *2 =    │    |  |
  2209.                 │  |  |                     │    +--+
  2210.                 │  +--+                     │
  2211.                 └───────────────            └───────────────
  2212.  
  2213.     So, what do we need to do ?
  2214.     Check out this case:
  2215.  
  2216.                    │                              │
  2217.                    │                              │
  2218.                    │                            +---+
  2219.                   +-+                           | │ |
  2220.           ────────|┼|────────    *2=     ───────|─┼─|────────
  2221.                   +-+                           | │ |
  2222.                    │                            +---+
  2223.                    │                              │
  2224.                    │                              │
  2225.  
  2226.     Have you figured out what you have to do already ?
  2227.     You have to move the object to the center of the world (point [0,0,0]), to
  2228.   be able to scale it properly... Procedure to scale:
  2229.  
  2230.                    Procedure Scale(Var Obj:Object3d;XScl,YScl,ZScl:Real);
  2231.                    Var A:Byte;
  2232.                    Begin
  2233.                         For A:=1 To Obj.NumberPoints Do
  2234.                         Begin
  2235.                              Obj.Pt[A].X:=Trunc(Obj.Pt[A].X*XScl);
  2236.                              Obj.Pt[A].Y:=Trunc(Obj.Pt[A].Y*YScl);
  2237.                              Obj.Pt[A].Z:=Trunc(Obj.Pt[A].Z*ZScl);
  2238.                         End;
  2239.                    End;
  2240.  
  2241.     This procedure can have other uses... You can shrink an image, by
  2242.   specifying a scaling factor smaller than 1. If you specify a negative number,
  2243.   the object will be inverted. If you don't want to scale a certain axis,
  2244.   specify 1, not 0, because 0 would nulify that coordinates... Now, let's see
  2245.   scaling in a matrix form:
  2246.  
  2247.                      X = Xo*XScl
  2248.                      Y = Yo*YScl
  2249.                      Z = Zo*ZScl
  2250.  
  2251.     Where X, Y and Z are the scaled coordinates; Xo, Yo and Zo are the original
  2252.   coordinates and XScl, YScl and ZScl are the scaling factors... Putting this
  2253.   in matrix form:
  2254.  
  2255.                ┌   ┐   ┌                ┐┌  ┐   ┌       ┐
  2256.                │ X │   │ XScl  0    0   ││Xo│   │Xo*XScl│
  2257.            P = │ Y │ = │  0   YScl  0   ││Yo│ = │Yo*YScl│
  2258.                │ Z │   │  0    0   ZScl ││Zo│   │Zo*ZScl│
  2259.                └   ┘   └                ┘└  ┘   └       ┘
  2260.  
  2261.     See ? Easy... :)
  2262.     Now we can see the use for matrixes. Imagine that some transformation
  2263.   involved translation AND scaling. You can do both in just one step... All
  2264.   you have to do is multiply the matrixes that do these transformations. So,
  2265.   matrix for this operation is:
  2266.  
  2267.   ┌                 ┐ ┌                  ┐ ┌                                 ┐
  2268.   │ 1    0    0   0 │ │ XScl  0    0   0 │ │   XScl        0         0     0 │
  2269.   │ 0    1    0   0 │ │  0   YScl  0   0 │ │     0       YScl        0     0 │
  2270.   │ 0    0    1   0 │*│  0    0   ZScl 0 │=│     0         0       ZScl    0 │
  2271.   │XOff YOff ZOff 1 │ │  0    0    0   1 │ │ XOff*XScl YOff*YScl ZOff*ZScl 1 │
  2272.   └                 ┘ └                  ┘ └                                 ┘
  2273.  
  2274.     Applying a vector:
  2275.  
  2276. ┌                                 ┐┌  ┐ ┌                                      ┐
  2277. │   XScl        0         0     0 ││Xo│ │               Xo*XScl                │
  2278. │     0       YScl        0     0 ││Yo│ │               Yo*YScl                │
  2279. │     0         0       ZScl    0 ││Zo│=│               Zo*ZScl                │
  2280. │ XOff*XScl YOff*YScl ZOff*ZScl 1 ││1 │ │Zo*XOff*ZScl+Yo*YOff*ZScl+Zo*ZOff*ZScl│
  2281. └                                 ┘└  ┘ └                                      ┘
  2282.  
  2283.     In equation formula:
  2284.  
  2285.                 X=(Xo+XOff)*XScl;
  2286.                 Y=(Yo+YOff)*YScl;
  2287.                 Z=(Zo+ZOff)*ZScl;
  2288.  
  2289.     This is what you expected to found... In this case, matrix multiplication
  2290.   isn't very useful, but in cases with lots of transformations, including
  2291.   rotations and other things like that, this becomes very useful !
  2292.     Now, let's move on to the other type of transformation, and probably one
  2293.   of the most importants... It can be even more important than translation.
  2294.   But it is also harder... Yep, I'm talking about rotations...
  2295.     We'll first analise the case of planar rotation, that is, a rotation that
  2296.   is in order to a plane, or (if you prefer) an axis... I'll explain later how
  2297.   to generalize this to 3d rotation...
  2298.  
  2299.     So, in a rotation, you want to transform point (x,y) in a point (x',y').
  2300.   Below is an example of a 45 degrees rotation:
  2301.  
  2302.                 Y                           Y
  2303.                 │                           │
  2304.                 │                           O (x',y')
  2305.                 │   O (x,y)                 |
  2306.                 │  /                 -->    |
  2307.                 │ /                         |
  2308.                 │/alpha                     |beta
  2309.                 └───────────────X           └───────────────X
  2310.  
  2311.     Alpha is the angle the point makes with the X axis... In this case, it is
  2312.   45 degrees. Beta is the angle the transformed point makes with the X axis,
  2313.   in this case, 90 degrees. Notice that if this was a 3d rotation, it would be
  2314.   a rotation around the Z axis... That means that the Z coordinate of the points
  2315.   is never changed. Let's do some formulas to get to the final one...
  2316.     We know (or if you don't know, get an 8th grade book and read the trigono-
  2317.   metry part) that a point can be expressed by an (X,Y) ordinated pair or
  2318.   a (P,A) pair, in which p is the distance of the point to the axis and a is
  2319.   the angle the point makes with one of the other axis. We also know the
  2320.   relations between the two systems of coordinates:
  2321.  
  2322.                           X = P*Cos(A)
  2323.                           Y = P*Sin(A)
  2324.  
  2325.                           P = Sqrt(X^2+Y^2)
  2326.                           A = ArcCos(X/P) = ArcSin(Y/P)
  2327.  
  2328.     Sqrt is the square root...
  2329.     The (X,Y) coordinate system is called the carthesian system, and the (P,A)
  2330.   system is called the polar coordinates system.
  2331.     Let's imagine we have point A, with carthesian coordinates (X,Y). That would
  2332.   convert in polar coordinates (P1,A1), like this:
  2333.  
  2334.                           P1 = Sqrt(X^2+Y^2)
  2335.                           A1 = ArcCos(X/P1) = ArcSin(Y/P1)
  2336.  
  2337.   and we want to rotate the point by an angle B. That would get us point C, with
  2338.   polar coordinates (P2,A2). As we can see from the above example (and from simple
  2339.   maths), P1 is equal to P2. Now, we want to obtain the carthesian coordinates
  2340.   of point C. So, from the above formulas we know that the carthesian coordinates
  2341.   of point C are:
  2342.                            X' = P1*Cos(A2)
  2343.                            Y' = P1*Sin(A2)
  2344.  
  2345.     But, we also know that A2=A1+B, because B is the angle we rotated and A1 is
  2346.   the original angle. So:
  2347.                            X' = P1*Cos(A1+B)
  2348.                            Y' = P1*Sin(A1+B)
  2349.  
  2350.     Again, with a simple knowledge of trigonometry, we know that:
  2351.  
  2352.                          Sin(U+V) = Sin(U)*Cos(V) + Cos(U)*Sin(V)
  2353.                          Cos(U+V) = Cos(U)*Cos(V) - Sin(U)*Sin(V)
  2354.  
  2355.     So, applying to the formula above:
  2356.  
  2357.                         X' = P1*[Cos(A1)*Cos(B)-Sin(A1)*Sin(B)]
  2358.                         Y' = P1*[Sin(A1)*Cos(B)+Cos(A1)*Sin(B)]
  2359.  
  2360.     From above, we know that:
  2361.  
  2362.                         A1 = ArcCos(X/P1) = ArcSin(Y/P1)
  2363.  
  2364.     And from that, we derive that:
  2365.  
  2366.                             Cos(A1) = X/P1
  2367.                             Sin(A1) = Y/P1
  2368.  
  2369.     So, applying in the other formula:
  2370.  
  2371.                     X' = P1*[(X/P1)*Cos(B)-(Y/P1)*Sin(B)]
  2372.                     Y' = P1*[(Y/P1)*Cos(B)+(X/P1)*Sin(B)]
  2373.  
  2374.     And again, applying simple maths, we get the final formula:
  2375.  
  2376.                           X' = X*Cos(B) - Y*Sin(B)
  2377.                           Y' = Y*Cos(B) + X*Sin(B)
  2378.  
  2379.     See ? It is fairly easy to calculate... Now, let's do a simple procedure:
  2380.  
  2381.                Procedure RotateZ(Var Obj:Object3d;Deg:Integer);
  2382.                Var A:Byte;
  2383.                    Angle:Real;
  2384.                    XTemp:Real;
  2385.                Begin
  2386.                     Angle:=0.0175*Deg;
  2387.                     For A:=1 To Obj.NumberPoints Do
  2388.                       With Obj.Pt[A] Do
  2389.                       Begin
  2390.                            XTemp:=X;
  2391.                            X:=XTemp*Cos(Angle)-Y*Sin(Angle);
  2392.                            Y:=Y*Cos(Angle)+XTemp*Sin(Angle);
  2393.                       End;
  2394.                End;
  2395.  
  2396.     Angle is the equivalent to Deg, but expressed in radians. We need that
  2397.   because the Sin and Cos functions use radians. We must use a temporary
  2398.   variable, because the X value is altered in the first expression, but we
  2399.   need X's old value for the second expression...
  2400.     As usual, let's move this to matrix form... We need a 2x2 matrix:
  2401.  
  2402.                           X' = X*Cos(Ang) + Y*Sin(Ang)
  2403.                           Y' = Y*Cos(Ang) - X*Sin(Ang)
  2404.  
  2405.     The matrix corresponding to these formulas is:
  2406.  
  2407.                           ┌                     ┐
  2408.                           │ Cos(Ang)  -Sin(Ang) │
  2409.                           │ Sin(Ang)   Cos(Ang) │
  2410.                           └                     ┘
  2411.  
  2412.     Adapting this to a 3d rotation, we'll get:
  2413.  
  2414.                           ┌                        ┐
  2415.                           │ Cos(Ang)  -Sin(Ang)  0 │
  2416.                           │ Sin(Ang)   Cos(Ang)  0 │
  2417.                           │    0          0      1 │
  2418.                           └                        ┘
  2419.  
  2420.     Remember what I said about the Z coordinate being unaltered by the
  2421.   rotation ? That's why the Z line isn't altered (the Z column is the line),
  2422.   and Z doesn't enter any of the calculations (the Z collumn isn't altered).
  2423.     Now, if you change the name of the axis, you can get the formulas for the
  2424.   rotation around the other axis... The formulas, matrixes and procedures that
  2425.   achieve these effects follow...
  2426.  
  2427.                 Around the X axis:
  2428.  
  2429.                           Z' = Z*Cos(Ang) + Y*Sin(Ang)
  2430.                           Y' = Y*Cos(Ang) - Z*Sin(Ang)
  2431.  
  2432.                           ┌                        ┐
  2433.                           │ 1     0          0     │
  2434.                           │ 0  Cos(Ang)  -Sin(Ang) │
  2435.                           │ 0  Sin(Ang)   Cos(Ang) │
  2436.                           └                        ┘
  2437.  
  2438.                Procedure RotateX(Var Obj:Object3d;Deg:Integer);
  2439.                Var A:Byte;
  2440.                    Angle:Real;
  2441.                    ZTemp:Real;
  2442.                Begin
  2443.                     Angle:=0.0175*Deg;
  2444.                     For A:=1 To Obj.NumberPoints Do
  2445.                       With Obj.Pt[A] Do
  2446.                       Begin
  2447.                            ZTemp:=Z;
  2448.                            Z:=ZTemp*Cos(Angle)-Y*Sin(Angle);
  2449.                            Y:=Y*Cos(Angle)+ZTemp*Sin(Angle);
  2450.                       End;
  2451.                End;
  2452.  
  2453.  
  2454.                 Around the Y axis:
  2455.  
  2456.                           X' = X*Cos(Ang) + Z*Sin(Ang)
  2457.                           Z' = Z*Cos(Ang) - X*Sin(Ang)
  2458.  
  2459.                           ┌                        ┐
  2460.                           │ Cos(Ang)  0  -Sin(Ang) │
  2461.                           │    0      1      0     │
  2462.                           │ Sin(Ang)  0   Cos(Ang) │
  2463.                           └                        ┘
  2464.  
  2465.                Procedure RotateY(Var Obj:Object3d;Deg:Integer);
  2466.                Var A:Byte;
  2467.                    Angle:Real;
  2468.                    XTemp:Real;
  2469.                Begin
  2470.                     Angle:=0.0175*Deg;
  2471.                     For A:=1 To Obj.NumberPoints Do
  2472.                       With Obj.Pt[A] Do
  2473.                       Begin
  2474.                            XTemp:=X;
  2475.                            X:=XTemp*Cos(Angle)-Z*Sin(Angle);
  2476.                            Z:=Z*Cos(Angle)+XTemp*Sin(Angle);
  2477.                       End;
  2478.                End;
  2479.  
  2480.     You can see in the file 3D1.PAS an example program of all the rotations
  2481.   being made, one at a time.
  2482.     Notice that we have to move the objects to the origin of the world, in
  2483.   order for the rotation work correctly... That's because the origin (0,0,0)
  2484.   is the center of rotation... If you want another point to be the origin,
  2485.   you have to calculate the coordinates of the points of the object relative to
  2486.   that point, rotate those relative coordinates, and then calculate those
  2487.   coordinates relatively to the origin of the world. That's easier to do than
  2488.   it sounds... Just some subtractions and addictions...
  2489.     Now, imagine you wanted to rotate the object in the three axis... You just
  2490.   rotated around the various axis, using the formulas... To do so, you could
  2491.   use the following procedure:
  2492.  
  2493.                Procedure Rotate(Var Obj:Object3d;XRot,YRot,ZRot:Integer);
  2494.                Begin
  2495.                     RotateX(Obj,XRot);
  2496.                     RotateY(Obj,XRot);
  2497.                     RotateZ(Obj,XRot);
  2498.                End;
  2499.  
  2500.     In 3D2.PAS you can see an example of this...
  2501.     Another way you can do the three rotations is by joining the three matrixes
  2502.   of rotation together... Like this:
  2503.  
  2504.      ┌                    ┐ ┌                    ┐ ┌                    ┐
  2505.      │ Cos(a)  -Sin(a)  0 │ │ Cos(a)  0  -Sin(a) │ │ 1    0        0    │
  2506.      │ Sin(a)   Cos(a)  0 │*│   0     1     0    │*│ 0  Cos(a)  -Sin(a) │
  2507.      │   0        0     1 │ │ Sin(a)  0   Cos(a) │ │ 0  Sin(a)   Cos(a) │
  2508.      └                    ┘ └                    ┘ └                    ┘
  2509.  
  2510.     I'm not in the mood for arithmetic now... So, do the calculations
  2511.   yourself... It's a good exercice...
  2512.     To speed up the routines a bit, you can consider precalculating the
  2513.   values of the Sin(a) and Cos(a), because they will be very used in the
  2514.   routine. Other thing you can do to speed up the calculations is to use a
  2515.   pregenerated sine/cosine table... See 3D3.PAS to see an example...
  2516.     Now, let's move on to another subject...
  2517.  
  2518.     6.3. A 3D starfield
  2519.  
  2520.     The 3d starfield is one of the more used effects in yesterdays demos... As
  2521.   the matter of fact, there are lot's of demos that include starfields nowadays,
  2522.   with some new twist... Well, let's move on... What's a 3d starfield ?
  2523.     A 3d starfield is a starfield similar to the one I showed you in a previous
  2524.   issue of 'The Mag', but instead of just moving sideways, in the 3d starfield
  2525.   you are moving into it... You see the stars passing by...
  2526.     This is so simple to do... You just have an array of points, and you move
  2527.   them (using a translation), while you can rotate it around the Z axis, in
  2528.   order to get a cooler effect... Other thing you can do is to change the
  2529.   color of the point... The point can be darker if it is further away from the
  2530.   viewer and it gets brighter as it comes nearer.
  2531.     Other 3d starfields move around inside the starfield, but that's simple to
  2532.   do... It's just movement...
  2533.     This is so easy to make, that I'm gonna just splat the code here... Look
  2534.   at it and it will be clear...
  2535.  
  2536.                Program Starfield;
  2537.  
  2538.                Uses Mode13h,Crt;
  2539.  
  2540.                Const Points=200;
  2541.                      Speed=15;
  2542.                      RotateSpeed=1;
  2543.  
  2544.                Type Point3d=Record
  2545.                                   X,Y,Z:Real;
  2546.                             End;
  2547.  
  2548.                Var Stars:Array[1..Points] of Point3d;
  2549.                    A:Integer;
  2550.                    X,Y:Integer;
  2551.                    Color:Byte;
  2552.                    D:Char;
  2553.  
  2554.                Procedure Conv3d(P:Point3d;Var Xt,Yt:Integer);
  2555.                Begin
  2556.                     Xt:=160+Trunc((P.X*256)/P.Z);
  2557.                     Yt:=100+Trunc((P.Y*256)/P.Z);
  2558.                End;
  2559.  
  2560.                Procedure RotateZ(Deg:Integer);
  2561.                Var Angle:Real;
  2562.                    XTemp:Real;
  2563.                    S,C:Real;
  2564.                Begin
  2565.                     Angle:=0.0175*Deg;
  2566.                     S:=Sin(Angle);
  2567.                     C:=Cos(Angle);
  2568.                     For A:=1 To Points Do
  2569.                     Begin
  2570.                          XTemp:=Stars[A].X;
  2571.                          Stars[A].X:=XTemp*C-Stars[A].Y*S;
  2572.                          Stars[A].Y:=Stars[A].Y*C+XTemp*S;
  2573.                     End;
  2574.                End;
  2575.  
  2576.                Begin
  2577.                     { Setup graphics }
  2578.                     InitGraph;
  2579.                     InitVirt;
  2580.                     Cls(0,VGA);
  2581.                     Cls(0,VP[1]);
  2582.                     { Setup grayscale }
  2583.                     For A:=0 To 15 Do SetColor(A,A*4,A*4,A*4);
  2584.                     { Setup stars }
  2585.                     For A:=1 To Points Do
  2586.                     Begin
  2587.                          Stars[A].X:=Random(640)-320.0;
  2588.                          Stars[A].Y:=Random(400)-200.0;
  2589.                          Stars[A].Z:=Random(800);
  2590.                     End;
  2591.                     { Main cicle }
  2592.                     Repeat
  2593.                           { Move stars }
  2594.                           For A:=1 To Points Do Stars[A].Z:=Stars[A].Z-Speed;
  2595.                           { Rotate stars }
  2596.                           RotateZ(RotateSpeed);
  2597.                           { Check if any stars are very near of the viewer...
  2598.                             If they are, reset them... }
  2599.                           For A:=1 To Points Do If Stars[A].Z<=100 Then
  2600.                                                   Stars[A].Z:=800;
  2601.                           { Clear virtual screen }
  2602.                           Cls(0,VP[1]);
  2603.                           { Draw stars in virtual screen }
  2604.                           For A:=1 To Points Do
  2605.                           Begin
  2606.                                Conv3d(Stars[A],X,Y);
  2607.                                { Determine color }
  2608.                                Color:=15-(Trunc((16*Stars[A].Z)) Div 1000);
  2609.                                { The following procedure is equal to the
  2610.                                  normal putpixel, except that this check if
  2611.                                  the point is in the boundaries of the
  2612.                                  screen }
  2613.                                PutClippedPixel(X,Y,Color,VP[1]);
  2614.                           End;
  2615.                           { Copy virtual screen to VGA screen }
  2616.                           CopyPage(VP[1],VGA);
  2617.                     Until Keypressed;
  2618.                     { Shutdown }
  2619.                     CloseVirt;
  2620.                     Closegraph;
  2621.                End.
  2622.  
  2623.     This is easy to understand... All the concepts were given back in this
  2624.   article... Instead of drawing lines, you just put points... This could be
  2625.   speeded up by using assembler... It is so easy to code a starfield in ASM !
  2626.     But let's move on to another cool thing...
  2627.  
  2628.     6.4. VectorBalls
  2629.  
  2630.     What are vector balls ?
  2631.     Well, I could explain what are vector balls, but it's easier to show.
  2632.   Execute program VECTOR1.PAS and you'll see...
  2633.     It's a cool effect... It's name comes from the fact that they are balls,
  2634.   and that they use vectors... What is a vector ? A vector is a set of
  2635.   coordinates (in this case, three) that define a point in space or a
  2636.   direction... This is not a 'by the book' explanation, but it will have to
  2637.   do.
  2638.     Vectorballs have (x,y,z) coordinates that represent they're position in
  2639.   space. All you have to do is convert it's 3d coordinates to 2d coordinates
  2640.   and draw the ball... We use 2 diferent colors for the balls just to achieve
  2641.   an cooler effect...
  2642.     The only catch of vectorballs is that you must sort the vectorballs before
  2643.   drawing, so that the balls that are further away are draw first, so that
  2644.   those that are in front overwrite. If you don't do this, the effect wouldn't
  2645.   look like 3d at all... Try removing the call to the Sort procedure in the
  2646.   DrawBalls procedure... We used QuickSort, because it's the faster method,
  2647.   and speed is important in this effect.
  2648.     This is a fairly easy to do program, and it is all comented, so you
  2649.   shouldn't have dificulty in understanding it...
  2650.     If you wanted to do objects with more than two colors, it is better to
  2651.   use a base sprite and add the number of the color. If you don't understand
  2652.   what I'm talking about, check out the VECTOR2.PAS program... Check the
  2653.   DrawSprite routine in that one. That program also uses a procedure called
  2654.   LoadVector, that reads for disk a file containing the data for the color
  2655.   and base coordinates of the vectorballs. That data is generated by the
  2656.   VECTGEN.PAS program.
  2657.     You can also add movement, by using the Move procedure... Movement in
  2658.   the procedure is cool...
  2659.     This is the end of the first article on 3d in 'The Mag'... I don't know
  2660.   when I'll do the next part, but it will be about poligons and sorting...
  2661.   And maybe a little bit of lighting... But try to figure this out for
  2662.   yourself... :)
  2663.  
  2664.  
  2665. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  2666.  
  2667.  
  2668.   7. Sprites Part III - Lots of fun stuff
  2669.  
  2670.     Hello again... Let's get going with sprites in this article... This is
  2671.   gonna be a quick one, because I want to wrap this issue of 'The Mag' quickly,
  2672.   'cos this is more than 120Kb already...
  2673.  
  2674.     7.1. Transparency
  2675.  
  2676.     Have you noticed that when you put down a sprite, someparts of it obscures
  2677.   what it was there before ?
  2678.     Well, to solve that, we use transparency... I already talked about
  2679.   transparency in the Fonts article I did last issue... I will just go over
  2680.   the algorithm... The ideia is this: if the color of the pixel to place is 0,
  2681.   then don't put down that pixel, in the other case, put it down. Simple and
  2682.   easy... You aren't limited to color 0... It can be any color... 0 just became
  2683.   the standart... Procedure talking:
  2684.  
  2685.       Procedure PutImage_T(X,Y:Word;Var Img:Pointer;Where:Word);
  2686.       Var Dx,Dy:Word;
  2687.           A,B:Word;
  2688.           Segm,Offs:Word;
  2689.       Begin
  2690.            Segm:=Seg(Img^);
  2691.            Offs:=Ofs(Img^);
  2692.            Move(Mem[Segm:Offs],Dx,2);
  2693.            Move(Mem[Segm:Offs+2],Dy,2);
  2694.            Offs:=Offs+4;
  2695.            For A:=Y To Y+Dy-1 Do
  2696.            Begin
  2697.                 For B:=X To X+Dx-1 Do
  2698.                 Begin
  2699.                      If Mem[Segm:Offs]<>0 Then
  2700.                        PutPixel(B,A,Mem[Segm:Offs],Where);
  2701.                      Inc(Offs);
  2702.                 End;
  2703.            End;
  2704.       End;
  2705.  
  2706.       Procedure PutImage_CT(X,Y:Integer;Var Img:Pointer;Where:Word);
  2707.       Var Dx,Dy:Word;
  2708.           A,B:Word;
  2709.           Segm,Offs:Word;
  2710.       Begin
  2711.            Segm:=Seg(Img^);
  2712.            Offs:=Ofs(Img^);
  2713.            Move(Mem[Segm:Offs],Dx,2);
  2714.            Move(Mem[Segm:Offs+2],Dy,2);
  2715.            Offs:=Offs+4;
  2716.            A:=Y;
  2717.            While (A<=Y+DY-1) And (A<MaxY) Do
  2718.            Begin
  2719.                 B:=X;
  2720.                 While (B<=X+DX-1) And (B<MaxX) Do
  2721.                 Begin
  2722.                      If (X>=MinX) And (Y>=MinY) And (Mem[Segm:Offs]<>0) Then
  2723.                        PutPixel(B,A,Mem[Segm:Offs],Where);
  2724.                      Inc(Offs);
  2725.                      Inc(B);
  2726.                 End;
  2727.                 Inc(A);
  2728.            End;
  2729.       End;
  2730.  
  2731.     There are two procedures, as you may already founded out... PutImage_T is
  2732.   for putting images using transparency and no-clipping. PutImage_CT is
  2733.   identically, but it performs clipping.
  2734.     The disadvantages of using transparency is that the procedures become
  2735.   slower. In order of speed:
  2736.  
  2737.                       PutImage       - No clipping / No transparency
  2738.                       PutImage_T     - No clipping / Transparency
  2739.                       PutImage_C     - Clipping / No transparency
  2740.                       PutImage_CT    - Clipping / Transparency
  2741.  
  2742.     You should choose carefully the type of PutImage you use, for the sake of
  2743.   speed... For example, to put down tiles, use PutImage, because you don't
  2744.   need transparency... Just use your head, before using the procedures.
  2745.     Just a note... We shouldn't call this transparency, but masking...
  2746.   Transparency is another thing, I will cover that in a future issue...
  2747.  
  2748.     7.2. Moving over a background
  2749.  
  2750.     In almost every game sprites are required... Usually they move above a
  2751.   background of some sort (take for example the mouse pointer... That can
  2752.   be thought as a sprite). Wouldn't it be great that the sprite could be moved
  2753.   without destroying the background ? Of course it would... In some computers
  2754.   (like the AMIGA), there is a chip that does this for us... In the PC, we'll
  2755.   have to do it ourselfs... This is easy...
  2756.     All you have to do is to grab a piece of image of the same size as the
  2757.   sprite, in the same location you will put the sprite down. Then, you put the
  2758.   sprite down... Every time you erase the sprite, instead of blacking the zone
  2759.   were the sprite was, you put the image you previously grabbed, so that
  2760.   everything remains the same. Then, you start all over again... Let's check
  2761.   some code:
  2762.  
  2763.                 Program SpritesOverBackground;
  2764.  
  2765.                 Uses Crt,Sprites,Mode13h;
  2766.  
  2767.                 Type Sprite=Record
  2768.                                   Img:Pointer;
  2769.                                   Back:Pointer;
  2770.                                   X,Y:Integer;
  2771.                             End;
  2772.  
  2773.                 Var F:File;
  2774.                     Ship:Sprite;
  2775.                     C:Char;
  2776.  
  2777.                 Begin
  2778.                      { Load sprite image }
  2779.                      Assign(F,'Ship.Img');
  2780.                      Reset(F,1);
  2781.                      LoadImage(F,Ship.Img);
  2782.                      Close(F);
  2783.                      { Init graphics and set palette }
  2784.                      InitGraph;
  2785.                      InitVirt;
  2786.                      SetColor(1,63,0,0);
  2787.                      SetColor(2,63,40,0);
  2788.                      SetColor(3,63,63,0);
  2789.                      { Load a background }
  2790.                      LoadPCX('Planet.Pcx',VP[1]);
  2791.                      SetPalette(PCXPal);
  2792.                      { Init ship }
  2793.                      Ship.X:=0;
  2794.                      Ship.Y:=100;
  2795.                      { Move the ship }
  2796.                      Repeat
  2797.                            { Get a 27x10 square of the place were the ship is }
  2798.                            GetImage(Ship.X,Ship.Y,Ship.X+27,Ship.Y+10,
  2799.                                     Ship.Back,VP[1]);
  2800.                            { Put sprite of ship, using masking }
  2801.                            PutImage_T(Ship.X,Ship.Y,Ship.Img,VP[1]);
  2802.                            { Copy virtual screen to VGA screen }
  2803.                            CopyPage(VP[1],VGA);
  2804.                            { Restore background }
  2805.                            PutImage(Ship.X,Ship.Y,Ship.Back,VP[1]);
  2806.                            KillImage(Ship.Back);
  2807.                            { Move ship }
  2808.                            Ship.X:=Ship.X+2;
  2809.                      Until (Ship.X>292) Or (KeyPressed);
  2810.                      C:=ReadKey;
  2811.                      KillImage(Ship.Img);
  2812.                      CloseVirt;
  2813.                      CloseGraph;
  2814.                 End.
  2815.  
  2816.     Notice the use of virtual screens... Almost everything that involves sprites
  2817.   must use virtual screens, in order to smooth the movements... Try not using
  2818.   virtual screens in the above program and see the crappy result...
  2819.  
  2820.     7.3. Flipping a sprite
  2821.  
  2822.     Wouldn't it be cool if the ship in the end would get back and forth ?
  2823.     Of course it would... But that would require two images, one pointing
  2824.   right and the other pointing left... Unless you use flipping... Flipping is
  2825.   a routine that inverts an image, like a mirror... This is easy to do, just
  2826.   think a bit... The first column would be the last column, the second column
  2827.   would be the 2nd last column, etc... This is horizontal flipping... For
  2828.   vertical flipping, the 1st line would become the last line, etc...
  2829.     Coding the horizontal flipping is easy:
  2830.  
  2831.                 Procedure FlipHoriz(Var Img:Pointer);
  2832.                 Var Dx,Dy:Word;
  2833.                     S1,O1:Word;
  2834.                     S2,O2:Word;
  2835.                     Tmp:Pointer;
  2836.                     A,B:Word;
  2837.                 Begin
  2838.                      { Get X and Y sizes }
  2839.                      S1:=Seg(Img^);
  2840.                      O1:=Ofs(Img^);
  2841.                      Move(Mem[S1:O1],Dx,2);
  2842.                      Move(Mem[S1:O1+2],Dy,2);
  2843.                      { Create temporary sprite }
  2844.                      GetMem(Tmp,Dx*Dy+4);
  2845.                      S2:=Seg(Tmp^);
  2846.                      O2:=Ofs(Tmp^);
  2847.                      { Put the size of the sprite in the temporary sprite }
  2848.                      Move(Mem[S1:O1],Mem[S2:O2],4);
  2849.                      { Move the columns }
  2850.                      For A:=0 To Dx-1 Do
  2851.                        For B:=0 To Dy-1 Do
  2852.                          Move(Mem[S1:O1+(B*Dx+A+4)],
  2853.                               Mem[S2:O2+(B*Dx+(Dx-A-1)+4)],1);
  2854.                      { Kill old image }
  2855.                      KillImage(Img);
  2856.                      { Copy new image to old one }
  2857.                      Img:=Tmp;
  2858.                 End;
  2859.  
  2860.     The vertical flipping is even easier:
  2861.  
  2862.                 Procedure FlipVert(Var Img:Pointer);
  2863.                 Var Dx,Dy:Word;
  2864.                     S1,O1:Word;
  2865.                     S2,O2:Word;
  2866.                     Tmp:Pointer;
  2867.                     A:Word;
  2868.                 Begin
  2869.                      { Get X and Y sizes }
  2870.                      S1:=Seg(Img^);
  2871.                      O1:=Ofs(Img^);
  2872.                      Move(Mem[S1:O1],Dx,2);
  2873.                      Move(Mem[S1:O1+2],Dy,2);
  2874.                      { Create temporary sprite }
  2875.                      GetMem(Tmp,Dx*Dy+4);
  2876.                      S2:=Seg(Tmp^);
  2877.                      O2:=Ofs(Tmp^);
  2878.                      { Put the size of the sprite in the temporary sprite }
  2879.                      Move(Mem[S1:O1],Mem[S2:O2],4);
  2880.                      { Move the lines }
  2881.                      For A:=0 To Dy-1 Do
  2882.                        Move(Mem[S1:O1+(A*Dx+4)],
  2883.                             Mem[S2:O2+((Dy-1-A)*Dx+4)],Dx);
  2884.                      { Kill old image }
  2885.                      KillImage(Img);
  2886.                      { Copy new image to old one }
  2887.                      Img:=Tmp;
  2888.                 End;
  2889.  
  2890.     All these routines are included in the Sprites unit... You can see a test
  2891.   program that makes the ship go right and left above a background in the file
  2892.   FLYSHIP.PAS.
  2893.     Cya in the next article...
  2894.  
  2895.  
  2896. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  2897.  
  2898.  
  2899.   8. Graphics Part IX - Polygons
  2900.  
  2901.     Polygons is a comes from a greek word that means 'many angles'. Speaking in
  2902.   general terms, a polygon is a planar set of points joined by lines that is
  2903.   closed. By closed, I mean that the last point joins with the first point.
  2904.     Circles and ellipses are special polygons, because they have an infinite
  2905.   number of points.
  2906.     Arcs are just here because I want to explain them. It's just a derivation
  2907.   of ellipses.
  2908.     For now forth, when I talk about polygons, I'm talking about 4 sided
  2909.   polygons, but that concept can be extended to any number of sides. For three
  2910.   sided polygons (triangles) this gets easier, and usually more than four sides
  2911.   aren't used.
  2912.  
  2913.     8.1. Simple polygons
  2914.  
  2915.     Four sided polygons are constructed of 4 points, each one of them having
  2916.   a (x,y) coordinate. Point 1 is joined with a line to point 2, point 2 to
  2917.   point 3, point 3 to point 4 and finally, point 4 to point 1. This is easy
  2918.   to do:
  2919.  
  2920.        Procedure Poly(X1,Y1,X2,Y2,X3,Y3,X4,Y4:Word;Color:Byte;Where:Word);
  2921.        Begin
  2922.             Line(X1,Y1,X2,Y2,Color,Where);
  2923.             Line(X2,Y2,X3,Y3,Color,Where);
  2924.             Line(X3,Y3,X4,Y4,Color,Where);
  2925.             Line(X4,Y4,X1,Y1,Color,Where);
  2926.        End;
  2927.  
  2928.     See how easy it was ? Now, let's move on to something more complex...
  2929.  
  2930.     8.2. Filled polygons
  2931.  
  2932.     The ideia behind filled polygons is fairly simple. You just draw horizontal
  2933.   lines between the edges of the polygon. Like this:
  2934.  
  2935.                     2        Check out the horizontal lines.
  2936.                     /\       The hardest thing in this way of filling polygons
  2937.                    /--\3     (because they are several different ways of doing
  2938.                   /---/      this) is to determine the minimum and maximum
  2939.                  1\--/       X coordinates, which we use to draw the horizontal
  2940.                    \/        line.
  2941.                     4
  2942.  
  2943.     In the 8th grade (and in a previous issue) you probably learned that any
  2944.   line can be defined by the equation:
  2945.  
  2946.                              (y-y0)=m(x-x0)
  2947.  
  2948.     As we want to know the X coordinate, let's solve the equation in order to
  2949.   X:
  2950.                                (y-y0)
  2951.                            x=  ------ + x0
  2952.                                  m
  2953.  
  2954.     As you probably know, the m is the tangent of the line, and it is defined
  2955.   as:
  2956.                            x1-x0
  2957.                         m= -----
  2958.                            y1-y0
  2959.  
  2960.     So, the final formula is:
  2961.  
  2962.                              (y-y0)*(x1-x0)
  2963.                           x= -------------- + x0
  2964.                                  (y1-y0)
  2965.  
  2966.     So, for any line in the polygon (in the above example, from the Y coordinate
  2967.   of point 2 to the Y coordinate of point 4), you must determine which is the
  2968.   line from which you will derive the X coordinate, to see if it is the maximum
  2969.   and minimum. To know which is the line, you simply compare see if the Y
  2970.   coordinate you are checking is between the Y coordinates of the various
  2971.   points. For example, imagine that the points in the example above had the
  2972.   following coordinates:
  2973.  
  2974.                           1: X1=5  ; Y1=25
  2975.                           2: X2=20 ; Y2=10
  2976.                           3: X3=30 ; Y3=20
  2977.                           4: X4=15 ; Y4=35
  2978.  
  2979.     And you wanted to know the maximmum and minimum X for line 17.
  2980.     Comparing the Y values, you know that line 17 will intersect the line
  2981.   between points 1 and 2, and the line between points 2 and 3. So, using the
  2982.   above coordinate, you know that the X coordinates of line 17 in those lines
  2983.   will be respectively 13 and 27. Now, you just had to draw a horizontal line
  2984.   between (13,17) to (27,17). The procedure to do the polygon follows:
  2985.  
  2986.        Procedure FPoly(X1,Y1,X2,Y2,X3,Y3,X4,Y4:Word;Color:Byte;Where:Word);
  2987.        Var MnY,MxY:Word;
  2988.            DeltaX1,DeltaX2,DeltaX3,DeltaX4:Integer;
  2989.            DeltaY1,DeltaY2,DeltaY3,DeltaY4:Integer;
  2990.            Y:Word;
  2991.            MnX,MxX:Integer;
  2992.            X:Integer;
  2993.        Begin
  2994.             { Find out the lines that are to be scanned }
  2995.             MnY:=Y1;
  2996.             MxY:=Y1;
  2997.             If MnY>Y2 Then MnY:=Y2;
  2998.             If MnY>Y3 Then MnY:=Y3;
  2999.             If MnY>Y4 Then MnY:=Y4;
  3000.             If MxY<Y2 Then MxY:=Y2;
  3001.             If MxY<Y3 Then MxY:=Y3;
  3002.             If MxY<Y4 Then MxY:=Y4;
  3003.             { Vertical clipping }
  3004.             If MnY<0 Then MnY:=0;
  3005.             If MxY>199 Then MxY:=199;
  3006.             If MnY>199 Then Exit;
  3007.             If MxY<0 Then Exit;
  3008.             { Precalculate the (x1-x0) and (y1-y0) needed later, because they
  3009.               remain the same for all the routine. They are needed to calculate
  3010.               the M. We don't calculate the M, because that would involve real
  3011.               maths that is slower than calculating in an integer form later
  3012.               in the code }
  3013.             DeltaX1:=(X1-X4); DeltaY1:=(Y1-Y4);
  3014.             DeltaX2:=(X2-X1); DeltaY2:=(Y2-Y1);
  3015.             DeltaX3:=(X3-X2); DeltaY3:=(Y3-Y2);
  3016.             DeltaX4:=(X4-X3); DeltaY4:=(Y4-Y3);
  3017.             { Main loop }
  3018.             For Y:=MnY To MnX Do
  3019.             Begin
  3020.                  { Find out the minummum and maximmum X coord }
  3021.                  MnX:=319;
  3022.                  MxX:=-1;
  3023.                  { Check if the line intersects line (X1,Y1)->(X2,Y2) }
  3024.                  If (Y>=Y1) Or (Y>=Y2) Then
  3025.                    If (Y<=Y1) Or (Y<=Y2) Then
  3026.                      { Insure that the points don't have the same Y coord }
  3027.                      If Not(Y1=Y2) Then
  3028.                      Begin
  3029.                           { Point of intersection }
  3030.                           X:=(Y-Y1)*DeltaX2 Div DeltaY2 + X1;
  3031.                           If X<MnX Then MnX:=X;
  3032.                           If X>MxX Then MxX:=X;
  3033.                      End;
  3034.                  { Check if the line intersects line (X2,Y2)->(X3,Y3) }
  3035.                  If (Y>=Y2) Or (Y>=Y3) Then
  3036.                    If (Y<=Y2) Or (Y<=Y3) Then
  3037.                      { Insure that the points don't have the same Y coord }
  3038.                      If Not(Y2=Y3) Then
  3039.                      Begin
  3040.                           { Point of intersection }
  3041.                           X:=(Y-Y2)*DeltaX3 Div DeltaY3 + X2;
  3042.                           If X<MnX Then MnX:=X;
  3043.                           If X>MxX Then MxX:=X;
  3044.                      End;
  3045.                  { Check if the line intersects line (X3,Y3)->(X4,Y4) }
  3046.                  If (Y>=Y3) Or (Y>=Y4) Then
  3047.                    If (Y<=Y3) Or (Y<=Y4) Then
  3048.                      { Insure that the points don't have the same Y coord }
  3049.                      If Not(Y3=Y4) Then
  3050.                      Begin
  3051.                           { Point of intersection }
  3052.                           X:=(Y-Y3)*DeltaX4 Div DeltaY4 + X3;
  3053.                           If X<MnX Then MnX:=X;
  3054.                           If X>MxX Then MxX:=X;
  3055.                      End;
  3056.                  { Check if the line intersects line (X4,Y4)->(X1,Y1) }
  3057.                  If (Y>=Y4) Or (Y>=Y1) Then
  3058.                    If (Y<=Y4) Or (Y<=Y1) Then
  3059.                      { Insure that the points don't have the same Y coord }
  3060.                      If Not(Y4=Y1) Then
  3061.                      Begin
  3062.                           { Point of intersection }
  3063.                           X:=(Y-Y4)*DeltaX1 Div DeltaY1 + X4;
  3064.                           If X<MnX Then MnX:=X;
  3065.                           If X>MxX Then MxX:=X;
  3066.                      End;
  3067.                  { Horizontal range checking }
  3068.                  If MnX<0 Then MnX:=0;
  3069.                  If MxX>319 Then MxX:=319;
  3070.                  { Draw the line }
  3071.                  If MnX<MxX Then Line(MnX,Y,MxX,Y,Color,Where);
  3072.             End;
  3073.        End;
  3074.  
  3075.     This looks very complicated, but it isn't... It's just confusing... This
  3076.   could be speeded up by using fixed point maths (I'll explain that in the
  3077.   next issue).
  3078.  
  3079.     8.3. Ellipses and Arcs
  3080.  
  3081.     Ellipses are a variation of circles... This is not a correct statement.
  3082.   Circles are a variation of ellipses is more correct.
  3083.     Ellipses are curved regions of space that have an horizontal and a vertical
  3084.   diameter. Circles are when these two dimension are identical. The only
  3085.   difference between the Ellipse and Circle procedures is that the X and Y
  3086.   factors are multiplied by different factors in the Ellipse procedure.
  3087.   Check the code out:
  3088.  
  3089.             Procedure Ellipse(X,Y,RH,RV:Integer;Col:Byte;Where:Word);
  3090.             Var Px,Py:Integer;
  3091.                 Deg:Word;
  3092.             Begin
  3093.                  For Deg:=0 to TableElements Do
  3094.                  Begin
  3095.                       Px:=Trunc(RH*Sines^[Deg]+X);
  3096.                       Py:=Trunc(RV*Cosines^[Deg]+Y);
  3097.                       PutPixel(Px,Py,Col,Where);
  3098.                  End;
  3099.             End;
  3100.  
  3101.     It's this easy... Don't forget to initialize the sine/cosine tables.
  3102.     Arcs are a variation of this. Here you specify a start and finish angle.
  3103.  
  3104.             Procedure Arc(X,Y,RH,RV:Integer;SAngle,EAngle:Integer;
  3105.                           Col:Byte;Where:Word);
  3106.             Var Px,Py:Integer;
  3107.                 Deg:Word;
  3108.             Begin
  3109.                  { Convert the angles to table positions }
  3110.                  SAngle:=Trunc(TableElements/360 * SAngle);
  3111.                  EAngle:=Trunc(TableElements/360 * EAngle);
  3112.                  { Draw the arc }
  3113.                  For Deg:=SAngle to EAngle Do
  3114.                  Begin
  3115.                       Px:=Trunc(RH*Sines^[Deg]+X);
  3116.                       Py:=Trunc(RV*Cosines^[Deg]+Y);
  3117.                       PutPixel(Px,Py,Col,Where);
  3118.                  End;
  3119.             End;
  3120.  
  3121.     Again, this is easy... Now, let's move on to:
  3122.  
  3123.     8.4. Filled Ellipses
  3124.  
  3125.     This is very easy to do... Just follow the theory behind the polygons:
  3126.   find the minimmum and maximum X value for each line... This is easy in
  3127.   the case of ellipses. You just mirror one side of it... Code:
  3128.  
  3129.             Procedure FEllipse(X,Y,RH,RV:Integer;Col:Byte;Where:Word);
  3130.             Var Px1,Px2,Py:Integer;
  3131.                 Delta:Integer;
  3132.                 Deg:Word;
  3133.             Begin
  3134.                  For Deg:=0 to (TableElements Div 2) Do
  3135.                  Begin
  3136.                       Delta:=Trunc(RH*Sines^[Deg]);
  3137.                       Px1:=Delta+X;
  3138.                       Px2:=X-Delta;
  3139.                       Py:=Trunc(RV*Cosines^[Deg]+Y);
  3140.                       Line(Px1,Py,Px2,Py,Col,Where);
  3141.                  End;
  3142.             End;
  3143.  
  3144.     Well, and this is the end of the polygons' article. See you the next issue,
  3145.   where I'll teach you how to optimize and transform this and all other stuff
  3146.   I gave in previous issues in assembly code. Cya...
  3147.  
  3148.  
  3149. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  3150.  
  3151.  
  3152.   9. Hints and Tips
  3153.  
  3154.                 *   - Begginners tip
  3155.                 **  - Medium tip
  3156.                 *** - Advanced tip
  3157.  
  3158.     - Not tottally random numbers (**)
  3159.  
  3160.       Did you knew that Pascal's Random function doesn't give out random
  3161.       values ?
  3162.       As the matter of fact, there's no way a computer can give out random
  3163.       numbers. That would only be possible if the computer thinked by itself,
  3164.       and as we all know, he can't... So, how are "random" numbers formed ?
  3165.       Well... They are made by using a formula (a very complex formula,
  3166.       sometimes, a simpler one other times) which uses a base number, known as
  3167.       seed to generate the numbers.
  3168.       So, a neat trick to create the same sequence of random numbers everytime
  3169.       you run the program is to use the same seed. To do that in Pascal, you
  3170.       use the system variable RandSeed...  For example, if you put this in
  3171.       your program:
  3172.                        RandSeed:=1000;
  3173.  
  3174.       the seed for your numbers will be 1000... So, the sequence will be same
  3175.       everytime you run the program. Check out the article about sorting and
  3176.       see that we've used this to create the same sequence of numbers for all
  3177.       sorting procedures. This can be very usefull for games... Especially
  3178.       games were are various things that are made randomly...
  3179.       For example, if you want to create an entire galaxy... Well, a galaxy in
  3180.       Pascal could only have 65536 positions, each of these positions only
  3181.       ocupying one byte, signifying something... Using the trick of the pseudo-
  3182.       -random numbers, that number could be the seed for a section of the
  3183.       galaxy, increasing even further the size ! You could do this recursively,
  3184.       until you have the size you want !!!! Cool, isn't it ?...
  3185.  
  3186.     - Typecasting (*)
  3187.  
  3188.       Pascal has an annoying thing... I think C has the same problem. It goes
  3189.       like this... If you have a statement like this:
  3190.  
  3191.                           A:=X*200;
  3192.  
  3193.       If you define X as an integer, the result of this operation will be an
  3194.       integer, even if you define A as a Longint. So, what's the problem ?
  3195.       The problem is if X is equal to (for example) 200. The result in A (in
  3196.       case A is a Longint) should be 40000, but instead, you will get the
  3197.       value -25536... Because the compiler stores the result in A as an
  3198.       integer, and not as a Longint. As the maximmum number allowed for an
  3199.       integer is 32768, the results go wrong when a calculation with an
  3200.       integer goes beyond this... A way to go around this is to do the
  3201.       following:
  3202.                           A:=LongInt(X)*200;
  3203.  
  3204.       This tells the compiler to do the calculations as it would do if X was
  3205.       a LongInt. You can do typecasting (this is the name for this) with any
  3206.       kind of variable. For example, you can do:
  3207.  
  3208.                           A:=Real(X)*200;
  3209.  
  3210.       Altough this would return an error, because A is a LongInt, and this
  3211.       would return a Real value.
  3212.  
  3213.  
  3214.     - With clause (*)
  3215.  
  3216.       Don't you, sometimes, get fed up of typing:
  3217.       thisvar.thisfield.thatfield.anotherfield:=10; ?
  3218.       That's why the With clause was invented... The With clause enables you
  3219.       to replace:
  3220.                  Var1.Field1.Field11:=1;
  3221.                  Var1.Field1.Field12:=2;
  3222.                  Var1.Field2.Field21:=3;
  3223.                  Var1.Field2.Field22:=4;
  3224.       by:
  3225.                 With Var1 Do
  3226.                 Begin
  3227.                      Field1.Field11:=1;
  3228.                      Field1.Field12:=2;
  3229.                      Field2.Field21:=3;
  3230.                      Field2.Field22:=4;
  3231.                 End;
  3232.  
  3233.       Simple, isn't it ?
  3234.  
  3235.  
  3236. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  3237.  
  3238.  
  3239.   10. Points of View
  3240.  
  3241.     Today's topic: three-dimensional graphics. Nowadays, a great controversy is
  3242.   installed. In one side, the purists, who are sick and tired of seeing 3d games
  3243.   and demos over and over again... On the other side, the 3d addicts, that love
  3244.   and can not live without 3d.
  3245.     I'm of a mild opinion... I like 3d games and I can enjoy a 3d sequence in a
  3246.   demo... What I can't stand is ALL games in 3d and ALL sequences in a demo
  3247.   in 3d.
  3248.     So, what's up with 3d ?
  3249.     The ideia is like this... Humans normally see the world in 3d. So, it's
  3250.   natural that a game in 3d is more realistic... People get into the game more
  3251.   easily. It's more natural...
  3252.     To keep this short, the bottom line is:  Don't exagerate !! Don't do DOOM
  3253.   clones over and over again... Do something more original... Like "Dark
  3254.   Forces", by LucasArts... And in demos, don't do 3d demos... Do demos with 3d,
  3255.   instead. Again, try something original... Don't keep a torus rotating two
  3256.   hours, just because it is texture-mapped and phong-shaded, with ten millions
  3257.   sides !!! Try to put a nice phong-shaded torus, with a 21bit plasma
  3258.   background, with a nice music pumping, and keep it there for 10 seconds
  3259.   only... Then move on to shadebobs or something completely different !!!
  3260.     So, what's in the next issue of 'The Mag' ?
  3261.   Well, we'll continue our series on Text Adventures (this time, monsters and
  3262.   special rooms). I'll continue the 3d tutorial (poligons and poligon sorting,
  3263.   and maybe light-sourcing), the sprites tutorial (rotation and anything I can
  3264.   think off) and the graphics tutorial (I'll delve into assembler). I'll do an
  3265.   article on optimization and cross-fading. I think they'll be another article
  3266.   by Scorpio (this is, if I can convince him to do one).
  3267.     Well, this was quite a large issue... More than 150 Kb in size !! That's
  3268.   big !!! That represents more than 150000 characters! 'The Mag' is growing... :)
  3269.     I don't know if you noticed, but it was more or less than a year ago that
  3270.   I made the first issue... So 'The Mag' has now one year of existence...
  3271.     It would be nice if someone writes an article on how 'The Mag' has evolved
  3272.   in the course of this year... (HINT, HINT)... :)
  3273.     Well, let's move on to:
  3274.  
  3275.  
  3276. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x
  3277.  
  3278.  
  3279.  11. The adventures of Spellcaster, the rebel programmer of year 2018.
  3280.  
  3281.   Episode 10 - Artificial Inteligence ?
  3282.  
  3283.   - His heart stopped... - I softly said to Kristie. - ...but his brain is
  3284.     still alive.
  3285.     Kristie tried hard to hold off the tears.
  3286.   - So what ?! - she shouted, angry.
  3287.   - I can do something... - I said, looking at the Karl's pale face.
  3288.   - What ? His heart is dead ! We can't take him to an hospital... - a bright
  3289.     tear rolled down her face.
  3290.   - I can save part of him... But it would cost him dearly...
  3291.   - What are you talking about ?!
  3292.   - He would have to loose his humanity... With the equipment we stole, I think
  3293.     I can transfer the contents of his mind to one of the new HexaMind
  3294.     computers. He would be as he ever was... But caged inside a machine.
  3295.   - I... I... don't know what to say... - she said, bowing her head.
  3296.   - Say yes... It's the only way to save him...
  3297.   - Are you sure you can do that ? - she asked, looking me directly in the
  3298.     eyes.
  3299.   - No... - I said, with a sad look on my face.
  3300.     Kristie sitted down on the floor... After a couple of minutes, she rised,
  3301.   approached Karl and looked down at his cold face. Then, she looked at the
  3302.   encefalogram and she hold Karl's hand.
  3303.   - Do it, Spell...
  3304.     An hour later, Karl's brain was hooked into the HexaMind computer, that was
  3305.   ready to read data. The HexaMind computers were an analog computer, of a new
  3306.   technologie, similar in design to our brain.
  3307.     I pressed a switch and suddently, Karl's body was shaking because of the
  3308.   huge amount of electricity it was being forced to hold.
  3309.     Minutes later, the operation was finished. The encefalogram showed zero
  3310.   activity. Or Karl was dead, or he was in the computer.
  3311.   - Karl ?... Can you ear me ? - Kristie said, approaching the large computer.
  3312.   - Kristie ?... - a synthesized voice asked. - Where am I ? I can't see you ?
  3313.   - I'll hook up a camera... - I said, reaching for a slot, plugging in the
  3314.     DeltaMatrox chip that controlled a video camera attached to it.
  3315.   - I can see again... - the voice said again, seeming lost.
  3316.     After half an hour, the explanations were made. Karl seemed calm and happy
  3317.   to be alive. Kristie was happy too... I was not. I had the burden in my soul.
  3318.   The burden of the guilt of playing God...
  3319.     I went outside, looking at the red sky above. It was near sunset. Just a
  3320.   couple of thoughts went throught my mind.
  3321.   - Is it right to destroy his humanity ? Can I play God like this ?
  3322.  
  3323.                                                See you in the next issue
  3324.                                               Diogo "SpellCaster" Andrade